I have a situation in which it can happen, that the last strong reference to an observer is removed while the observer processes an incoming notification.
That leads to the observer being deallocated immediately. I would normally expect, that a currently running method can finish before an object is deallocated. And this is what happens during normal message dispatch.
A simplified version of the code:
TKLAppDelegate.h:
#import <UIKit/UIKit.h>
#import "TKLNotificationObserver.h"
@interface TKLAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) TKLNotificationObserver *observer;
@end
TKLAppDelegate.m:
#import "TKLAppDelegate.h"
@implementation TKLAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Create an observer and hold a strong reference to it in a property
self.observer = [[TKLNotificationObserver alloc] init];
// During the processing of this notification the observer will remove the only strong reference
// to it and will immediatly be dealloced, before ending processing.
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationName" object:nil];
// Create an observer and hold a strong reference to it in a property
self.observer = [[TKLNotificationObserver alloc] init];
// During the manual calling of the same method the observer will not be dealloced, because ARC still
// holds a strong reference to the message reciever.
[self.observer notificationRecieved:nil];
return YES;
}
@end
TKLNotificationObserver.m:
#import "TKLNotificationObserver.h"
#import "TKLAppDelegate.h"
@implementation TKLNotificationObserver
- (id)init {
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationRecieved:) name:@"NotificationName" object:nil];
}
return self;
}
- (void)notificationRecieved:(NSNotification *)notification {
[self doRemoveTheOnlyStrongReferenceOfThisObserver];
NSLog(@"returing from notification Observer");
}
- (void)doRemoveTheOnlyStrongReferenceOfThisObserver {
TKLAppDelegate * delegate = [[UIApplication sharedApplication] delegate];
delegate.observer = nil;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"NotificationName" object:nil];
NSLog(@"dealloc was called");
}
@end
Using the App Delegate in this way is no good style and only done for demonstration purposes, the real code does not involve the app delegate.
The output is:
dealloc was called
returing from notification Observer
returing from notification Observer
dealloc was called
That is in the first case dealloc is called before the notification processing finished. In the second case it behaves as I expected.
If I keep a strong reference to self inside notificationReceived
the dealloc only happens after the processing. My expectation was, that ARC, the runtime or whoever else keeps this strong reference for me.
What is wrong with my code?
Or is something wrong with my expectation?
Is there any Apple- or Clang-provided documentation on this?