Created
November 26, 2010 01:36
-
-
Save AlanQuatermain/716167 to your computer and use it in GitHub Desktop.
A demonstration of some potential issues around the use of -[NSNotificationCenter addObserver:...usingBlock:].
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@implementation MyObject | |
@synthesize observer; | |
- (void) storeObserverVariable | |
{ | |
self.observer = [[NSNotificationCenter defaultCenter] | |
addObserverForName: @"TheNotification" | |
object: nil | |
queue: [NSOperationQueue mainQueue] | |
usingBlock: ^(NSNotification * note) { | |
// this reference to 'self' within the block causes 'observer' | |
// to retain 'self' which retains 'observer', which retains the | |
// block, which retains 'self' ... | |
if ( [self validateContents] == NO ) | |
return; | |
[self sortContents]; | |
[self performLayout]; | |
}]; | |
} | |
- (void) stopObservingInsideBlock | |
{ | |
id tmpObserver = [[NSNotificationCenter defaultCenter] | |
addObserverForName: @"TheNotification" | |
object: nil | |
queue: [NSOperationQueue mainQueue] | |
usingBlock: ^(NSNotification * note) { | |
if ( [self validateContents] == NO ) | |
{ | |
// must remember to remove observer if there are multiple | |
// exit paths (which are often a bad thing in themselves, | |
// but...) | |
[[NSNotificationCenter defaultCenter] removeObserver: tmpObserver]; | |
return; | |
} | |
[self sortContents]; | |
[self performLayout]; | |
// we must remove observer from within this block, because | |
// nothing else has a reference to it otherwise. | |
[[NSNotificationCenter defaultCenter] removeObserver: tmpObserver]; | |
}]; | |
} | |
- (void) repeatObservationsWithoutMemberVariable | |
{ | |
id tmpObserver = [[NSNotificationCenter defaultCenter] | |
addObserverForName: @"TheNotification" | |
object: nil | |
queue: [NSOperationQueue mainQueue] | |
usingBlock: ^(NSNotification * note) { | |
if ( [self validateContents] == NO ) | |
{ | |
// must remember to remove observer if there are multiple | |
// exit paths (which are often a bad thing in themselves, | |
// but...) | |
[[NSNotificationCenter defaultCenter] removeObserver: tmpObserver]; | |
// I want to sign up again, getting another observer object | |
// to release on the next invocation -- but how can I | |
// reference the block that's currently executing to pass | |
// it into this function? | |
return; | |
} | |
[self sortContents]; | |
[self performLayout]; | |
// we must remove observer from within this block, because | |
// nothing else has a reference to it otherwise. | |
[[NSNotificationCenter defaultCenter] removeObserver: tmpObserver]; | |
// see above-- how do I re-register to keep receiving the | |
// events? | |
}]; | |
} | |
- (void) onlyFlawlessWayOfDoingIt | |
{ | |
typedef void (^notificationObserver_block)(NSNotification *); | |
// our observer block needs to reference itself, which means we | |
// need a variable to hold it which is defined up front. For | |
// clarity and safety (and sanity) we'll make it a __block | |
// variable: | |
__block notificationObserver_block observer_block = (notificationObserver_block)0; | |
// the observer needs to be assigned by the observer block when it | |
// signs up again, which means another __block variable: | |
__block id tmpObserver = nil; | |
// the observer block gets defined and assigned to its variable | |
// here: | |
observer_block = ^(NSNotification * note) { | |
if ( [self validateContents] ) | |
{ | |
[self sortContents]; | |
[self performLayout]; | |
} | |
// remove the existing observer | |
[[NSNotificationCenter defaultCenter] removeObserver: tmpObserver]; | |
// signup again, because we want to keep receiving this | |
// notification | |
tmpObserver = [[NSNotificationCenter defaultCenter] | |
addObserverForName: @"TheNotification" | |
object: nil | |
queue: [NSOperationQueue mainQueue] | |
usingBlock: observer_block]; // references currently-executing block | |
}; | |
// now we need to repeat the last line of our defined block to | |
// start the ball rolling: | |
tmpObserver = [[NSNotificationCenter defaultCenter] | |
addObserverForName: @"TheNotification" | |
object: nil | |
queue: [NSOperationQueue mainQueue] | |
usingBlock: observer_block]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In
stopObservingInsideBlock
, shouldn'tid tmpObserver
be__block id tmpObserver
? Otherwise it is released and isnil
in the notification block