这是我的模式:
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下解决这样的问题?
答案 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
中放置一个断点,看看它是否在方法返回之前被调用。