【问题标题】:View controller dealloc not called when using NSNotificationCenter code block method with ARC使用带有 ARC 的 NSNotificationCenter 代码块方法时未调用视图控制器 dealloc
【发布时间】:2012-09-23 19:51:50
【问题描述】:

当我在视图控制器的-viewDidLoad: 方法中将-addObserverForName: object: queue: usingBlock: 用于NSNotificationCenter 时,-dealloc 方法最终不会被调用。

(当我删除 -addObserverForName: object: queue: usingBlock: 时,-dealloc 会再次被调用。)

使用-addObserver: selector: name: object:似乎没有这个问题。我究竟做错了什么? (我的项目使用的是 ARC。)

下面是我的实现示例,以防我在这里做错了:

[[NSNotificationCenter defaultCenter] addObserverForName:@"Update result"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification *note) {
                                                  updateResult = YES;
                                              }];

提前感谢您的帮助。

我尝试添加以下内容(无济于事):

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    if ([self isMovingFromParentViewController]) {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
}

【问题讨论】:

    标签: iphone ios uiviewcontroller nsnotificationcenter dealloc


    【解决方案1】:

    updateResult 是一个实例变量,可防止对象在该块保留时被释放。

    换句话说,你有一个保留周期。对象保留块,块保留对象。

    您需要创建对该实例及其变量的弱引用或 unsafe_unretained 引用,以消除这种关系。

    在通知块之前添加以下内容:

    __unsafe_unretained YouObjectClass *weakSelf = self;
    

    或(如果您使用的是 iOS5 及更高版本)

    __weak YouObjectClass *weakSelf = self;
    

    然后,在该块中,通过新的弱引用引用该对象:

    [[NSNotificationCenter defaultCenter] addObserverForName:@"Update result"
                                                      object:nil
                                                       queue:nil
                                                  usingBlock:^(NSNotification *note) {
                                                      weakSelf.updateResult = YES;
                                                  }];
    

    请注意,保留周期本身并不是一件坏事。有时您实际上希望它们发生。但在这些情况下,您可以确定循环将在特定时间后中断(例如动画块)。一旦块执行完毕并从堆栈中删除,循环就会中断。

    【讨论】:

    • 我有其他块调用像 [self updateType:@"someType"]; 这样的方法,它们有同样的问题。这里是否也发生了保留周期?另外,您能否更具体地说明如何“创建对该实例及其变量的弱引用或 unsafe_unretained 引用”?我在这方面还是比较新的......谢谢!
    • 是的,这也会创建一个保留周期。有时这样的循环是完全没问题的——但只有在块被保证执行的情况下。一旦块完成执行(例如动画块),保留循环将被打破。
    • 我现在必须将updateResult 设为属性吗?或者有没有办法将其保留为实例变量?
    • 这个答案对我帮助很大。我注意到不使用ARC时没有这样的问题。我想知道为什么?对象仍然要保留自己,而自己也要保留对象。
    • @IPhoneGuy:遇到同样的问题。 Apple 的文档“Transitioning to ARC Release Notes”有以下引用:“在手动引用计数模式下,__block id x; 具有不保留 x 的效果。在 ARC 模式下,__block id x; 默认保留 x(就像所有其他值)。”也许这有帮助?
    【解决方案2】:

    这很可能是因为您有一个保留周期。

    这通常是当您的块隐式保留自我,而自我以某种方式保留块时的情况。您将有一个保留周期,因为每个都保留另一个,因此它们的 retainCount 永远不会达到零。

    您应该激活警告-Warc-retain-cycles,它会针对此类问题向您发出警告。

    因此,在您的情况下,您使用的是变量updateResult,我假设它是一个实例变量,这隐含地保留self。你应该使用一个临时的弱变量来表示 self,并在你的块中使用它,这样它就不会被保留,你会打破保留循环。

    __block __weak typeof(self) weakSelf = self; // weak reference to self, unretained by the block
    [[NSNotificationCenter defaultCenter] addObserverForName:@"Update result"
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification *note) {
                                                  // Use weakSelf explicitly to avoid the implicit usage of self and thus the retain cycle
                                                  weakSelf->updateResult = YES;
                                              }];
    

    【讨论】:

      【解决方案3】:

      这不是保留循环。

      NSNotificationCenter 持有区块,区块持有 self。因为[NSNotificationCenter defaultCenter]是一个生活在所有应用生命周期中的单例,所以它间接持有self

      【讨论】:

        猜你喜欢
        • 2014-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-19
        • 1970-01-01
        • 2012-12-01
        • 1970-01-01
        相关资源
        最近更新 更多