Dealloc Not Running When Dismissing Modal View from Block

后端 未结 3 1972
南笙
南笙 2020-12-12 04:32

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

[[NSNotificationCenter defaultCenter] addObserverForName:@\"user.logi         


        
相关标签:
3条回答
  • 2020-12-12 05:13

    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 讨论(0)
  • 2020-12-12 05:19

    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.

    0 讨论(0)
  • 2020-12-12 05:23

    (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.

    0 讨论(0)
提交回复
热议问题