为什么NSNotification块中的NSArray(显然)从未在ARC环境中发布?

时间:2013-04-19 21:42:06

标签: ios objective-c nsarray automatic-ref-counting

我有一个视图控制器订阅其他地方广播的通知。这是一个大对象图的一部分,它有一些需要在子元素被释放时进行的清理。但是dealloc从未在其中一个孩子中被调用(ARC环境),这个(如果我理解的话)意味着某个地方仍然被保留,从而导致ARC即使在VC被解雇时也永远不会被释放。我已经将违规代码追溯到以下两行。第一个版本导致VC和子项永远不会被完全释放。在第二个版本中,一切正常,当这个VC被解雇时,一切都被解除了。

我的问题是为什么?有了ARC,我无法理解为什么这个额外的NSArray无法正常发布。

希望这是足够的代码。如果需要可以发布更多。

这是导致VC(和孩子等)永远不会被完全释放的版本:

// Subscribe to notifications that the number of trackables has changed
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
    if ([note.object isKindOfClass:[NSArray class]]) {
        NSArray *activeTrackableNames = note.object; // <-- offending line
        [self trackableUpdate:activeTrackableNames]; // <-- offending line
    } else {
        NSLog(@"Observer error. Object is not NSArray");
    }
}];

但是当我按照以下方式执行此操作时,在卸载视图时所有内容都会正确释放,因此在子VC上调用dealloc:

// Subscribe to notifications that the number of trackables has changed
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
    if ([note.object isKindOfClass:[NSArray class]]) {
        [self trackableUpdate:note.object]; // <-- seems to work
    } else {
        NSLog(@"Observer error. Object is not NSArray");
    }
}];

此VC中的一些其他相关方法可能(?)发挥作用:

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    self.trackablesVisible_private = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark - Target Handling
-(void)trackableUpdate:(NSArray *)trackablesArray {
    NSLog(@"Trackable Changed");
    if (([trackablesArray count] > 0) && [self.delegate respondsToSelector:@selector(foundValidTargets:)]) {
        [delegate foundValidTargets:trackablesArray];
    }
    self.trackablesVisible_private = trackablesArray;
}

我不明白这里发生了什么。有人可以赐教我吗?

谢谢!

修改

正如回复中所述,问题的一部分是在块中未能使用弱self的保留周期,但显然此处还有另一个问题,因为我正在使用通知中心。此处描述了解决方案:Dealloc Not Running When Dismissing Modal View from Block

我的最终工作代码如下:

@interface

__weak id observer;

loadView

__weak ThisViewController *blockSelf = self; // Avoids retain cycle
observer = [[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
    if ([note.object isKindOfClass:[NSArray class]]) {
        ThisViewController *strongSelf = blockSelf; // Avoids retain cycle
        NSArray *activeTrackableNames = note.object;
        [strongSelf trackableUpdate:activeTrackableNames];
    } else {
        NSLog(@"Observer error. Object is not NSArray");
    }
}];

并在viewWillDisappear

[[NSNotificationCenter defaultCenter] removeObserver:observer];

我还不完全理解为什么你必须保存返回的观察者对象,而不是仅仅删除self,但这可行。

1 个答案:

答案 0 :(得分:2)

你的区块中有一个保留周期...这就是你想要的

__weak MyViewControllerName *bSelf = self;
// Subscribe to notifications that the number of trackables has changed
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdatedNumberofTrackables object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
    if ([note.object isKindOfClass:[NSArray class]]) {
        **NSArray *activeTrackableNames = note.object;
        [bSelf trackableUpdate:activeTrackableNames];**
    } else {
        NSLog(@"Observer error. Object is not NSArray");
    }
}];

有关块和保留周期的更多信息,请查看http://zearfoss.wordpress.com/2012/05/11/a-quick-gotcha-about-blocks/