如何正确通知代理不再需要该实例?

时间:2012-01-13 00:55:42

标签: iphone ios ipad memory-management automatic-ref-counting

这是我的模式:

1)SpecialView创建一个MessageView并拥有一个强引用。

2)用户点击MessageView中的一个按钮,使其淡出。然后MessageView告诉它的委托,SpecialView,它完全消失了。

3)SpecialView发布MessageView。

问题在于:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    [self.delegate messageViewFadedOut:self]; // delegate releases us...
    // self maybe got deallocated... BOOM!
    // now what? method of a zombie returns? stack freaks out?
} // does it even return?

在最后一行,我正在调用委托,而委托又会立即释放MessageView。 -fadedOut:finished:context:由核心动画didStopSelector回调调用。

我担心,在-fadedOut:finished:context:完全返回之前,MessageView实例将被释放,导致非常恶劣的随机错误

曾几何时,一位资深的老同事告诉我:“永远不要砍掉你所坐的树枝。”

因此,为了确保实例在此方法返回之前存活,我在调用委托之前进行了retain-autorlease-dance:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    //[[self retain] autorelease];
    [self.delegate messageViewFadedOut:self]; // delegate releases us...
}

然而,在ARC下,不再允许保留自动释放舞蹈(迁移工具不允许)并且似乎无法强制ARC系统执行此类操作。

所以我想出了这个策略:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context {
    [self.delegate performSelector:@selector(messageViewFadedOut:) withObject:self afterDelay:0];
}

延迟的performSelector希望让方法完全返回。据我所知,延迟为0仍然保证选择器在下一次运行循环迭代中执行而不是立即执行。

这也很有可能是假的。

如何正确解决一个对象的问题,要求另一个对象销毁对它的最后一个引用,并且在调用另一个对象的方法有机会完全返回之前,对象被释放的可能性?可以有类似堆栈跟踪僵尸的东西吗?

我怎么能在ARC下解决这样的问题?

1 个答案:

答案 0 :(得分:0)

说实话,我认为不应该尝试模拟保留自动释放,而应该确保在调用委托方法messageViewFadedOut:时,您不关心是否拥有对您的消息的引用视图已发布。 willFadeOut: 方法的合同可能会假定它不会被取消分配,但 fadedOut:didFadeOut: 方法应该没问题对象被解除分配。

理想的解决方案是让你的dealloc方法通过取消正在发生的任何活动的淡出动画(在某处保留对它的引用)来专门避免你所害怕的恶意随机错误。这样你就不关心它到达dealloc的方式和时间,因为dealloc知道做X以防止在它死亡时留下任何对象状态或视觉异常。

所以只需使用您的解决方案(performSelector:withObject:afterDelay:)或GCD块:

- (void)fadedOut:(NSString*)animationID finished:(NSNumber*)finished context:(void*)context
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 250 * NSEC_PER_MSEC), // 250ms
                   dispatch_get_current_queue(),
                   ^{
                        [self.delegate messageViewFadedOut:self];
                   });
}

此外,ARC可能只是autorelease,你为什么不多次测试那段代码,看看会发生什么?在dealloc中放置一个断点,看看它是否在方法返回之前被调用。