开发者

Dealloc Not Running When Dismissing Modal View from Block

Strange one here, dealloc is not being called when dismissed from inside a block. Code:

[[NSNotificationCenter defaultCenter] addObserverForName:@"user.login" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {

[self dismissModalViewControllerAnimated:YES];

 }];

Anyone know why this would be the case? How can i dismiss from inside the block and run dealloc at the same time开发者_运维技巧?

I have tried self performselector but this did not make any difference.

Thanks


(1) Your code is wrong (incomplete). When you issue addObserverForName: you must capture the returned value; this is the observer token. You store this somewhere (e.g. an instance variable):

self->observer = [[NSNotificationCenter defaultCenter] 
    addObserverForName:@"woohoo" object:nil queue:nil 
    usingBlock:^(NSNotification *note) 
        {
            //whatever
        }];

Later, when you're going out of existence, you remove that observer token from the notification center by calling removeObserver: with that token as argument. If you don't do that, you can crash later.

[[NSNotificationCenter defaultCenter] removeObserver:self->observer];

(2) But wait, there's more! Under ARC, when the block is copied, you'll get a retain cycle. This is because the stored observer token contains the block and is itself retaining self. I will give you three ways to break this retain cycle:

(a) Store the observer token as a weak reference:

__weak id observer;

(b) Store the observer token as a strong reference, but explicitly release it (by nilifying it) when you remove the observer:

[[NSNotificationCenter defaultCenter] removeObserver:self->observer];
self->observer = nil; // crucial

(c) Do the "weak-strong dance", like this, when you create the block (I am pretending that self is a FlipsideViewController):

__weak FlipsideViewController* wself = self;
observer = [[NSNotificationCenter defaultCenter] 
             addObserverForName:@"user.login" 
             object:nil queue:nil usingBlock:^(NSNotification *note) {
    FlipsideViewController* sself = wself;
    [sself dismissModalViewControllerAnimated:YES];
}];

Now, you might think that the "weak-strong dance" is an extreme approach, as one of my commenters implies. But it has one huge advantage: it is the only one of these three solutions that allows you to remove the observer in dealloc. With the other two solutions, dealloc will never be called until after you have called removeObserver: - and finding a better place to call it may not be easy.


I refer you to: Reference Counting of self in Blocks

The block will retain self until the block is released. So to dealloc self, you'll need to remove the observer.


Could this be related to UIKit being not completely thread-safe? UIKit should be only used on the main thread...

If so, I would suggest using:

performSelectorOnMainThread:withObject:waitUntilDone:

(reference)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜