I'm stepping through some code in lldb, and I come across [alert show]. I step across it, nothing happens, then I continue and the alert pops up, presumably triggered sometime later. Just for curiosity's sake, when does that show message actually get sent to the operating system? What's really going on when I step over [alert show]? The documentation doesn't address it.
UIKit and core animation changes are processed and applied as part of the main run loop. When you call [alert show] the appropriate view hierarchy changes, frame changes, animations etc. are queued up in the system. When you return from your code the runloop will process these as part of the core animation transaction internals and you will see the changes on screen.
Related
i am working on a iOS application with accessibility support. At some point in my application flow, i present an alert view. After presenting the view, i want to focus on the view using UIAccessibilityPostNotification, however the notification seems to get overridden.
[alertView show];
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,alertView.somesubView);
However i do not see the effect of this notification. The accessibility focus goes to some other view object in the background.
However, when i use dispatch_after with 0 delay, it works
[alertView show];
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW,0 * NSEC_PER_SEC);
dispatch_after(delay,dispatch_get_main_queue(), ^void(){
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,alertView.somesubView);
});
Can someone explain what is the reason ?
You've stumbled upon the standard solution. It's likely that the user interface or accessibility hierarchies may not have updated, yet, to reflect the presence of the alert view. An async dispatch to the main queue ensures that all other enqueued tasks, including any updates to user interface or accessibility state, execute before the notification is posted.
That said, VoiceOver should focus alert views automatically. You might want to investigate what interfered with this behavior in the first place.
I'm slowly working on my first simple timer app, which is starting to slowly come to life. At the moment I've done no saving of data for when my app enters the background, for the moment whilst writing my app it is always staying loaded during my testing, even if its in the background for a small time. All this is being done on the simulator.
My problem is that I have a Table View Controller with an NSInteger index property
#property NSInteger index;
that I use to manage the slow iteration step by step over an NSArray over time, by calling a doWork method.
Initial setup of my TVC and this property is performed as the view is prepared for segue from another view. More work is done on this property and other properties whenever my app receives local notifications or upon observing UIApplicationWillEnterForegroundNotifications.
All accessing of this property I've added debug logging around and I'm just confused with what I'm seeing
PrepareForSegue
# 19:38:29:058 - calls a method doWork which increments my property from -1 to 0, and sets a local notification. Logs show self=0x10bb661f0
I press the home button to put my app in the background for ~10 secs
The local notification fires and I tap the banner to bring my app back to the focus
My TVC's awakeFromNib function sets up a notification observer
as follows
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillEnterForegroundNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
[self doWork];
}];
This notification fires twice! (for some reason?) each time causing my doWork method to be called
# 19:38:45:832 - doWork immediately shows my property is now back to -1 not the 0 it was set to before being backgrounded, it is incremented again to 0. Also it shows self as different at self=0x10974bbd0, a value it remains at now whilst the app is foregrounded.
# 19:38:45:836 - doWork is called again from this 2nd notification call, my property is correctly now still 0 from the last call, it is incremented again to 1.
after that my app delegate also gets it's didReceiveLocalNotification method called, which will also end up calling the same doWork method via another block in a Notification Center observer setup the same as the one detailed above.
# 19:38:45:857 - doWork is called and again the property has gone from the 1 that was set, back to 0... and is again incremented back to 1.
I just can't understand what's going on. Most of the properties in my TVC are still presumably fine as the rest of my logic showing the TVC contents continues to work fine. Why is my NSInteger getting mangled so?
I thought perhaps there could be some issues with threading and the local notification handling occurring perhaps simultaneously, I hoped that adding the mainQueue would help with this, I'd previously got this set to nil. Sadly it didn't make any difference.
I wonder why self changes after being backgrounded for only a short amount of time. Naively I presumed that as everything seemed to be working on coming back to the foreground (the view still displayed, with all its data seemingly intact aside from this NSInteger property) that the self ptr and object would be the same. It's not too much to imagine that perhaps its been relocated by the OS somehow, but still this shouldn't have caused the property to change.
I'm using cocoa lumberjack for logging, and I've turned off ASYNC logging as follows
#define LOG_ASYNC_ENABLED NO
Which should at least mean that log calls block till they've logged. I could appreciate that if i did have some threading issues perhaps the logging order would be slightly in doubt. But for the initial corruption of the property described above after the app enters the background there is ~10 seconds from when I write 0 to the property, and later read -1 back out of it. This is clearly not a threading timing issue at that point.
Why am I notified about entering the foreground twice? If my properties were left where I set them it wouldn't matter much, but still strikes me as a little odd.
Is there a problem with my using self within this block, I've seen that sometimes you may want to use a weak self pointers for these blocks, but I've seen examples where self is used directly as well.
Any help on understanding and hopefully fixing this would be really appreciated. I'm a little stuck and can't see what I've done wrong!
Cheers
So the answer to my problem as figured out by Woofbeans and Phillip Mills is that my awakeFromNib was being called each time i entered this TVC, and also I was failing to removeObserver on these notifications causing the stale undisplayed TVCs to stay around indefinitely. I'd not realised this key part of my repro of the problem.
Go into the TVC, come out, go back in and then you would have a duplicate of alerts in the app being caused by the fact that the original TVC was still around, being held onto by the strong reference to self within my own blocks.
I'm going to refactor this doWork functionality into my model so that it can persist and be handled better, independently of whatever view happens to be being displayed.
I also changed my block code to use a weakself pointer within the block, to hopefully stop that block from causing any object to persist purely because of the block being left behind.
Cheers everyone!
First of all Check This to understand what i am doing
I did not get my answer in above question & still waiting for it.
Now new porblem is that when i click on back button using following code, methods of last ViewController are still running. It will use memory & keep processing untill it gets response(that`s what i want to do but if user press back then i want to stop all methods)
[self.navigationController popViewControllerAnimated:YES];
How do i stop it?
You need to do [getTryAgainTask cancel] (assuming getTryAgainTask is of type NSURLSessionDownloadTask) before you pop this controller. The download task is asynchronous and runs irrespective of the controller (that fired it) being deallocated. This might cause retain loops, leading the app to eventually crash. The code as of now, will go into an infinite loop. A better solution would be to keep a tab on the number of retries (say 3 times) and then prompt the user about the problem, asking if he/she would like to try again.
I have in the past seen a UIAlertView freeze momentarily during its animation, but not have anything else onscreen freeze (such as when deleting apps from a device...the other apps keep on wiggling). In my app right now I've got a UIAlertView freezing for a moment partway through its animation, but the Time Profiler in Instruments doesn't show the kind of CPU usage that would freeze the UI, and I can see other things happening in the UI behind the alert. What could cause this?
Often this type of thing is caused by showing the alertView in the action method of some control. If this is your case, use a dispatch block that shows the UIAlert (which you can prepare in the action routine), and dispatch it async to the main queue.
I found interesting things.. Following code doesn't show #"One" and it show #"Two" after 3 seconds delay..
I think that #"One" need to be shown and then 3 seconds delay and then #"Two" need to pop up..
Am I wrong?
self.statusLabel.text = #"One";
sleep(3);
self.statusLabel.text = #"Two";
Thanks..
If you're doing this on the main thread, that sleep(3) will block it, freezing the app for 3 seconds. Event processing, including things like repainting the UI, won't happen til that's over.
To get what you're expecting, try something like this:
[self.statusLabel setText:#"One"];
[self.statusLabel performSelector:#selector(setText:)
withObject:#"Two"
afterDelay:3.0];
Does the first change, then queues up an invocation performing the second change to happen in the future. Then returns control to the OS to do any necessary redrawing.
Your notion of how things should work is incorrect.
self.statusLabel.text = #"One";
This sets the value of the statusLabel field to "One". This does not immediately draw to the screen. Instead, the label will mark itself as needing display. At the end of the current run loop cycle, all the views marked as needed display will be drawn, and then their contents flushed to the screen.
Next you do:
sleep(3);
self.statusLabel.text = #"Two";
This blocks the main thread for 3 seconds (never returning to the run loop to do the work mentioned above), then changes the value to "Two" which marks the view again as needing display.
When it is eventually drawn, and flushed to the screen, the current value is "Two".
It is hard to give more specific advice about what you should be doing, because it isn't clear if there is a real problem you are trying to solve, or just experimenting in order to learn more about the frameworks.
But you should almost never use sleep, and you certainly shouldn't be blocking the main thread with sleep for several seconds.
You're wrong...
Think of it this way: when you execute code in a block you're telling iOS what you want to do. It only actually implements your instructions after you pass control back to the OS.
Your code blocks the main thread (which is a very bad thing to do).
What you need to do is set the label to "One" then set a timer that will fire in three seconds time. The code in the timer would set the text of the label to "Two."