iOS Are methods called by delegates and observers executed on the main thread?
Asked Answered
D

3

17

Sorry, I'm not sure of the right language here, but when methods are called because they are either delegate methods, or methods called as a result of being listed as the target of an observer, are they executed on the main thread?

I'm wondering if I can just make UI changes in these methods, or do I have to wrap them in

    dispatch_async(dispatch_get_main_queue(), ^{ UI stuff });

TIA: John

Door answered 24/10, 2011 at 19:20 Comment(0)
D
13

For delegates this can vary. If the documentation does not specify, then usually they are sent on the main thread. Traditionally UIKit must be used on the main thread so those delegates will almost always be called from the main thread.

For notifications I think you want this little snip.

A notification center delivers notifications to observers synchronously. In other words, the postNotification: methods do not return until all observers have received and processed the notification. To send notifications asynchronously use NSNotificationQueue. In a multithreaded application, notifications are always delivered in the thread in which the notification was posted, which may not be the same thread in which an observer registered itself.

From http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsnotificationcenter_Class/Reference/Reference.html

And finally for KVO, the notifications can come in from other threads. Here is what an Apple Engineer had to say about handling them.

http://lists.apple.com/archives/cocoa-dev/2007/May/msg00022.html

Danielldaniella answered 24/10, 2011 at 19:25 Comment(6)
Also for NSURLConnection the delegate callbacks are on the thread that started the asynchronous load operation, which doesn't have to be the main thread.Adipocere
It sure would be nice if the docs made this clear, but for example, the UIImagePickerControllerDelegate Protocol Reference doesn't even use the word 'thread'. I haven't seen any issues with imagePickerController:didFinishPickingMediaWithInfo:, but I've suddenly become more aware of threading issues. Your snip was definitely helpful for notifications, which I use in my app. Again, my new thread-awareness got me wondering about them. I will have to go through them all to make sure they are all correct. Thanks!Door
You can assume that with UI objects that delegates are always on the main thread. The docs will specify if there is an exception, which I have never seen. You can infer this from the docs here. developer.apple.com/library/ios/documentation/uikit/reference/…Danielldaniella
@Door Did you read the Thread Programming Guide for iOS?Adipocere
I'm reading the Thread Programming Guide again right now. There sure are a lot of moving pieces in an iOS program. It's hard for newbies like me to remember them all, or even be aware of them all. I'm getting there, though.Door
Excuse me - but what about UNNotificationCenter notifications? These seem to be different. When debugging (my MacOS app) I always see them on the main queue - but CAN I RELY on this? since I'm focusing my UI on the object for which I get UNNotificationCenter delegate calls - I must do this on main thread.Constant
Y
9

As stated, the thread will vary based on the caller. In your delegate method, if you need to adapt, you can always do something like this:

if ([NSThread isMainThread]) {
    // do the UI stuff as normal
} else {
    dispatch_async(dispatch_get_main_queue(), ^{ UI stuff });
}
Yurev answered 24/10, 2011 at 19:27 Comment(3)
What will be the difference if I don't check the current thread and always dispatch asynchronously? So even if that delegate method is called on main thread, I will be dispatching, what are the cons of this? Or what are the pros of this solution (checking the current thread) ?Estriol
I don't think there would be any harm in doing the dispatch_async even if it is already on the main thread. You would have to experiment to make sure that the performance penalty of the dispatch_async method would be not that much worse than checking the NSThread isMainThread property.Yurev
The difference is - if you do this asynchronously - then the code after your block - will be called BEFORE the UI is updated, while if you do it synchronously - your subsequent code will be executed AFTER the UI is updated.Constant
S
3

The basic idea is that in all cases the observer or delegate method are called in the same thread the initial notification (for the observer pattern) or delegating code are running, so if you're not sure it's recommended to dispatch your UI block in the main thread. I will try to justify this statements in the reasoning below, of course I can be wrong.

Unless explicitly specified in the delegate protocol documentation, in the delegate pattern a method is called directly in the same thread the caller is running at the moment of the call. E.g. if the caller (delegating object) wants to call his delegate and is currently running on "Thread-1" then the call will happen in the same thread:


// this is running in "Thread-1" --> then aDelegateMethod will continue on "Thread-1"
[myDelegate aDelegateMethod]

As far as the observer pattern, I don't see any valid reason for the system to send an observing notification explicitly on the main thread, especially if the original value change that originates the notification is running in another thread. In fact in the KVO case the runtime changes the class definition by adding some private methods that override the setter methods to do the notifications, and I don't see a valid reason to do this call explicitly in the main thread. So according to me a KVO notification can originate from any thread and this thread is the same that is running the value change in the observed class.

Finally the NSNotificationCenter based mechanism sees his notifications called by the same thread where the original notification has been posted. This is clearly stated in Apple documentation (and it's worth to say that every thread has its own notification queue).

So in all cases the thread is maintained and if you want to be sure your UI block is called in the main queue then use the GCD call that you posted in your question.

Sheugh answered 24/10, 2011 at 20:2 Comment(1)
Re your last sentence: this seems to be the most straight forward way to deal with the issue. I've just gone through my code and made sure I do this in all cases. I ran into the issue because my app creates a video on a background thread. I wanted to disable a button on my UI while the video was being created, but this caused some squirrelly behavior. That in turn lead to my newly heightened awareness of threading issues :-)Door

© 2022 - 2024 — McMap. All rights reserved.