Does any body know what I need to check if app freezes after some time? I mean, I can see the app in the iPhone screen but no view responds.
I did some google and i found that, i've blocked the main thread somehow.
But my question is how to identify which method causes blocking of main thread? is there any way to identify?
Launch your app and wait for it to freeze. Then press the "pause" button in Xcode. The left pane should show you what method is currently running.
Generally, it is highly recommended to perform on the main thread all animations method and interface manipulation, and to put in background tasks like download data from your server, etc...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to perform in background
dispatch_async(dispatch_get_main_queue(), ^{
//call back to main queue to update user interface
});
});
Source : http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks
Set a break point from where the freeze occurs and find which line cause that.
Chances may be,Loading of large data,disable the controls,overload in main thread,Just find out where that occurs using breakpoints and rectify based on that.
I believe it should be possible to periodically check to see if the main thread is blocked or frozen. You could create an object to do this like so:
final class FreezeObserver {
private let frequencySeconds: Double = 10
private let acceptableFreezeLength: Double = 0.5
func start() {
DispatchQueue.global(qos: .background).async {
let timer = Timer(timeInterval: self.frequencySeconds, repeats: true) { _ in
var isFrozen = true
DispatchQueue.main.async {
isFrozen = false
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + self.acceptableFreezeLength) {
guard isFrozen else { return }
print("your app is frozen, so crash or whatever")
}
}
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.run()
}
}
}
Update October 2021:
Sentry now offers freeze observation, if you don't wanna roll this yourself.
I reached an error similar to this, but it was for different reasons. I had a button that performed a segue to another ViewController that contained a TableView, but it looked like the application froze whenever the segue was performed.
My issue was that I was infinitely calling reloadData() due to a couple of didSet observers in one of my variables. Once I relocated this call elsewhere, the issue was fixed.
Most Of the Time this happened to me when a design change is being called for INFINITE time. Which function can do that? well it is this one:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
Solution is to add condition where the function inside of viewDidLayoutSubviews get calls only 1 time.
It could be that another view is not properly dismissed and it's blocking user interaction! Check the UI Debugger, and look at the top layer, to see if there is any strange thing there.
Related
In my code I am executing a network call which takes few seconds to complete. While it is executing I want to display a progresshud in the background. But the problem is the progresshud does not appears before the network call. It appears right after the network call finishes. I can not understand the issue.
My code is below.
func draw() {
if !self.drawing) {
self.progressHud.show(in: self.view)
self.drawing = true
self.drawImage() // this is the function that takes time to execute
}
else if (self.isTransformViewEnabled){
self.drawing = false
}
}
Please help
I think you are calling the network call synchronously. So it waits the request to complete for showing hud. You can call in on Main thread on async
DispatchQueue.main.async {
//your code here
}
This is happening because you are making the network call on the main queue synchronously. Hence the UI is updated after the call is completed.
Please make the network call asynchronously in a different queue and the HUD will show up on the screen.
DispatchQueue.main.async {
self.drawImage()
}
Let me know if it works for you.
Happy to help.
Thanks.
So, on my app I have a feed that I want to update every time the user returns to the app.
I'm calling a routine to do this on applicationWillEnterForeground of my AppDelegate.
Everything is working fine but, sometimes, my UI freezes during this operation.
I was able to find where this occurs using a label to show the progress of this routine. The label is updated on three major points:
Before starting the routine
Inside the routine
When the routine ends
Sometimes, this workflow works fine, and I'm able to see the progress through this label.
But sometimes, the label only shows the first message, and messages that happen inside the routine does not appear. Besides that, I can't do anything on my app, because the UI is frozen. Once the routine is over, everything comes back to normal.
Here's a simplified version of the flow my app executes to call this routine:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
let mainViewController = MainViewController()
func applicationWillEnterForeground(_ application: UIApplication) {
mainViewController.refreshLatestVideos()
}
}
class MainViewController: UITabBarController {
private var subscriptionsController: SubscriptionsController! // initialized on viewDidLoad
func refreshLatestVideos() {
subscriptionsController.refreshLatestVideos(sender: nil)
}
}
class SubscriptionsController: UITableViewController {
private var subscriptionsModelController: SubscriptionsModelController! // received on constructor
#objc func refreshLatestVideos(sender:UIButton!) {
showMessage(message: "Updating subscriptions...") // this message is always shown to me
subscriptionsModelController.loadLatestVideos()
}
}
class SubscriptionsModelController {
func loadLatestVideos() {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
DispatchQueue.global(qos: .userInitiated).async {
// bunch of requests with Just
...
// update message
showMessage(message: "Updating subscription x of y") // this message sometimes doesn't appear, because the UI is frozen
// another requests
...
// update message
showMessage(message: "Updates completed")
}
}
}
As you can see, I'm executing the update inside the global queue, so I'm not blocking the main thread.
And again, this freezing of the UI only happens sometimes.
Is there any point which I can look at to find what's going on? Is it possible that the main thread is being blocked by something else?
Dispatch updating the UI to the main thread:
DispatchQueue.main.async { showMessage(message: "Updates completed") }
Everytime you anyhow access/modify the UI, do it on the main thread to avoid having unexpected problems (link to one of several resources on this topic, I suggest you google up and read more).
That applies to the rest of the code as well, if there is something that is related to UI, do the same for it - e.g., if after finishing the task you call tableView.reloadData, do it on the main thread too.
I'm a beginner in swift 2, and I'm trying to make my program blocks while showing only a progress spinner until some operation finishes, I made that code snippet in a button with the action "touch up inside", my problem is that while debugging,Xcode 7 CPU usage jumps to 190 % once I tap my button and keeps high until the flag changes its value, Is it normal that CPU usage jumps like that?, also Is it a good practice to use the following snippet or shud i use sleep or some other mechanism inside my infinite loop?
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(self.queue2) { () -> Void in
while(flag == true)
{
//wait until flag sets to false from previous func
}
self.dispatch_main({
//continue after the flag became false
})
This is a very economical completion handler
func test(completion:() -> ())
{
// do hard work
completion()
}
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
}
}
or with additional dispatch to the main queue to update the UI
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
dispatch_async(dispatch_get_main_queue()) {
// update UI
}
}
}
This is totally wrong approach as you are using while loop for waiting. You should use Completion Handler to achieve this kind of stuff.
Completion handlers are callbacks that allow a client to perform some action when a framework method or function completes its task. Often the client uses a completion handler to free state or update the user interface. Several framework methods let you implement completion handlers as blocks (instead of, say, delegation methods or notification handlers).
Refer Apple documentation for more details.
I suppose you have a sort of class which manages these "some operation finishes".
When you finish your operations you can comunicate by completion handler or delegation. In the meanwhile you can disable the user interaction of your UI until the end of these operations.
If you provide more informations about your background operations I can add some snippets of code.
I call a function to open a subview from my parent view.
I see that viewDidLoad is called. In my viewDidLoad is no code.
The strange behaviour is, that sometimes viewDidAppear: is not called even I have not change any code.
Of course there can be a lot of reasons, but I do not know how to debug. I am looking for a way to find out where the process hangs at the time when viewDidLoad is finished.
Does anyone have a hint for me or does anyone know what could be the problem? I mean sometimes it works sometime not. Is viewDidAppear: depending on the parentsview or is there something wrong in the subview?
func showSubview() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("Subview") as! SubViewController
self.presentViewController(vc, animated: true, completion: nil)
//self.showViewController(vc, sender: self);
}
UPDATE
I am calling the showSubview from a async task , I guess this is not good. What do you think and how shall I do it better ? Is there any complition handler with dispatch_async ?
dispatch_async(backgroundQueue, {
dosomething_long();
showSubview();
})
UPDATE 2
The problem is, that I am opening the subview from a background task. I should not do this. But the question is how can I call the subview when the background task is finished. Is there any completion handler for dispatch_async call ?
SOLVED WITH HELP OF GANDALF
I am stupid :-) I should call the subview in the main thread:
dispatch_async(backgroundQueue, {
dosomething_long();
dispatch_async(dispatch_get_main_queue(), {
showsubview();
});
})
The very obvious reason for why program counter may not go inside methods is that app could be running in the release mode.
Now as we can see after updated question that this is not the reason and there is a UI operation happening at background queue.
As per iOS development guidelines all UI operation should happen on main thread, you should try executing that on main thread instead of a background thread.
Try the below code:
dispatch_async(backgroundQueue, {
dosomething_long();
dispatch_async(dispatch_get_main_queue(), {
showsubview();
});
})
The question is; is the view created before? Is it caused when you reopen your app from background?
You can check the lifecycle from here and I think your problem is occurred because of that.
iOS 7 - Difference between viewDidLoad and viewDidAppear
The code is too huge to post it here. My problem is the following. When I call animateWithDuration:animations:completion: (maybe with options) with duration == 0.3 it doesn't mean that the completion block will be called through the same delay. It is called through 2 seconds instead and it is too long for me.
This big delay usually appears before memory warnings but sometimes may work as expected.
Could anybody explain what may cause such a strange behaviour?
Are there any timers involved, like is the animation timer-triggered?
I had a similar problem when my animation was timer-triggered. It turned out the animation was started more than once. animationOngoing flag stopped animation from being started again before finishing.
// Timer function
func timerTextToggle(timer: NSTimer) {
if self.animationOngoing == false {
self.flipAnimation()
}
}
// Animation function
func flipAnimation() {
// important note: it's UIViewAnimationOptions,
// not UIViewAnimationTransition
self.animationOngoing = true
if self.animationToggle == false {
UIView.transitionFromView(self.singleTapLabel!,
toView: self.doubleTapLabel!,
duration: animDuration,
options: UIViewAnimationOptions.TransitionFlipFromBottom,
completion: {
(value: Bool) in
self.animationOngoing = false
})
} else {
UIView.transitionFromView(self.doubleTapLabel!,
toView: self.singleTapLabel!,
duration: animDuration,
options: UIViewAnimationOptions.TransitionFlipFromTop,
completion: {
(value: Bool) in
self.animationOngoing = false
})
}
self.animationToggle = !self.animationToggle
}
I experienced a similar problem to this, although without further information on your scenario, I don't know if this also applies to your issue.
I was calling becomeFirstResponder on a UITextField in the completion block of my animateWithDuration:delay:options:animations:completion. Logging showed the completion block was being called in a timely manner, but the keyboard was taking several seconds to show. This was only occurring on first launch of the keyboard.
This answer helped me solve this... turned out this was somehow linked to the iOS Simulator. This issue did not occur when I wasn't debugging the app... another classic example of chasing a bug for hours in the simulator that didn't actually exist.
The cause of this problem is found out. It is a lot of UIWebView objects rendered in the main thread. And it seems impossible to prevent their loading. Anyways time profiler show that a lot of time is spent to render them even if they are not visible on the screen.
And yes, I can't release them before memory warning event because of requirements