objective-c - 仅在 iOS 4 上的 ARC 中为 "Modifying layer that is being finalized"

标签 objective-c ios xcode calayer automatic-ref-counting

在 iOS5 上,我的应用程序运行一整天,没有任何问题。在 iOS 4.3 上,只要我尝试切换 View ,应用程序就会开始喷出“修改正在完成的层”,指向刚刚解除分配的 View 的 CALayer,直到我终止程序或它自己崩溃。

我正在构建的应用有大约 8 个小游戏,每个游戏都包含一个 View 。我有一个主视图 Controller ,在那个类中我保留对当前游戏 View 的引用。

UIView* currentView;

主视图完全是空的。通过本质上调用此 View 将 View 加载到其中:

SomeView*someView = [[SomeView alloc]initWithFrame:self.bounds];
someView.delegate = self;
currentView = someView;
[self.view addSubview:currentView];

我注意到在 iOS 4 上,在 Dealloc 上调用了 removeFromSuperview,但在 iOS 5 上却没有。所以我的 dealloc 方法都是这样的:

NSLog(@"Dealloc Game Name");
if (([[[UIDevice currentDevice] systemVersion] floatValue] > 4.9)){
    [self removeFromSuperview];
}

似乎每次调用时都会调用 dealloc 方法

currentView = nil;

currentView = someOtherView;

这在 iOS4 和 iOS5 之间是一致的。

同样一致的是,如果我调用

[currentView removeFromSuperview]; 

currentView 中的 View 被释放,所以当我跟随它时

'currentView = nil;' or 'currentView = someOtherView;' or even '[self setCurrentView:bacon];'

应用程序崩溃,因为它试图向 currentView 中已发布的 View 发送另一个版本。

如果我关闭 NZZombies,我会从 EXC_BAD_ACCESS 崩溃中得到这个回溯。

2012-02-27 15:50:46.631 Keyboard[36378:207] Dealloc Splatter
2012-02-27 15:50:46.718 Keyboard[36378:207] modifying layer that is being finalized - 0x5a17900
(gdb) bt
#0  0x001e7b99 in CALayerCommitIfNeeded ()
#1  0x001e7bc4 in CALayerCommitIfNeeded ()
#2  0x001e7bc4 in CALayerCommitIfNeeded ()
#3  0x0018d4f1 in CA::Context::commit_transaction ()
#4  0x0018e294 in CA::Transaction::commit ()
#5  0x0018e46d in CA::Transaction::observer_callback ()
#6  0x0166889b in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#7  0x015fd6e7 in __CFRunLoopDoObservers ()
#8  0x015c61d7 in __CFRunLoopRun ()
#9  0x015c5840 in CFRunLoopRunSpecific ()
#10 0x015c5761 in CFRunLoopRunInMode ()
#11 0x021961c4 in GSEventRunModal ()
#12 0x02196289 in GSEventRun ()
#13 0x005f3c93 in UIApplicationMain ()
#14 0x000027d0 in main (argc=1, argv=0xbfffecc4) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/main.m:16

我的结果总是一样的,我试过用几种不同的方式声明 currentView,没有属性,也有作为属性。

@property(nonatomic,strong)__strong UIView* currentView;

@property(nonatomic,unsafe_unretained) UIView* currentView;

正如我通过搜索文档和 SO 了解到的那样,unsafe_unretained 属性在 dealloc 上应该为 nil,就像与 iOS 4 不兼容的弱引用一样?如果是这样,我一定是做错了,因为它仍然试图释放自己两次。

这些调用也都是在使用 performSelectorOnMainThread 调用的方法中进行的,因此我可能在任何时候都不在后台线程中。

我觉得我对 ARC 有一些低层次的误解,我自己无法解决这个问题。有什么想法吗?

哦,还有一件事。我用 -fno-objc-arc 标志编写的一款游戏在 iOS 4 上运行良好,我真的只是希望我不需要返回并将所有迷你游戏从 ARC 转换。

编辑更多信息: 有时我得到的不是 EXC_BAD_ACCES 错误,而是我认为它指向拥有 CALayer 的 UIView,该 CALayer 在“修改正在完成的图层”警告中指向:

malloc: *** error for object 0xa3012e4: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug

此外,分析器工具在分析干净的代码时会指出零可能的错误。

更新:

我完全按照 k1th 的建议做了,并逐步完成了代码。 唯一的区别是我使用了 self.currentView 而不是 currentView,并按照 Rob Napier 的建议在它的属性声明中添加了“readwrite”。 它基本上做了同样的事情。在调用 [currentView removeFromSuperview] 时,您看到的第一个“Dealloc Splatter”被打印出来。这是本次运行中 dealloc 中唯一的代码行,NSLog(@"Dealloc splatter");

在调用 currentView = someView 时,再次调用 Dealloc,它再次打印“Dealloc Splatter”,然后在到达函数末尾时迅速崩溃。这是堆栈跟踪。我已经通过逐行执行此代码三遍来验证了这一点。

2012-02-27 19:13:58.138 Keyboard[36828:207] call switch
2012-02-27 19:14:00.481 Keyboard[36828:207] SwitchViews
2012-02-27 19:14:13.980 Keyboard[36828:207] Dealloc Splatter
2012-02-27 19:14:20.234 Keyboard[36828:207] Dealloc Splatter
(gdb) bt
#0  0x01a43098 in objc_msgSend ()
#1  0x0061e361 in -[UIView dealloc] ()
#2  0x00025818 in -[CanvasView dealloc] (self=0xa330dd0, _cmd=0x57dfea2) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/Paint Splatter/CanvasView.m:54
#3  0x000062db in -[MenuViewController setCurrentView:] (self=0xa305170, _cmd=0x415f2, currentView=0x5c36920) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/MenuViewController.m:21
#4  0x00004ee9 in -[MenuViewController launchVisualizer:] (self=0xa305170, _cmd=0x41517, sender=0x0) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/MenuViewController.m:180
#5  0x00d6befc in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] ()
#6  0x00d7e506 in -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] ()
#7  0x0000423a in -[MenuViewController switchViews:] (self=0xa305170, _cmd=0x415d3, number=0x5c2c560) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/MenuViewController.m:70
#8  0x00d6c94e in __NSThreadPerformPerform ()
#9  0x016688ff in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#10 0x015c688b in __CFRunLoopDoSources0 ()
#11 0x015c5d86 in __CFRunLoopRun ()
#12 0x015c5840 in CFRunLoopRunSpecific ()
#13 0x015c5761 in CFRunLoopRunInMode ()
#14 0x021961c4 in GSEventRunModal ()
#15 0x02196289 in GSEventRun ()
#16 0x005f3c93 in UIApplicationMain ()
#17 0x00002890 in main (argc=1, argv=0xbfffecc4) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/main.m:16

在启用 NSZombies 的情况下执行此操作会得到以下结果,CanvasView 是 Splatter,0x5c2f020 也是:

2012-02-27 19:26:15.480 Keyboard[36856:207] call switch
2012-02-27 19:26:18.072 Keyboard[36856:207] SwitchViews
2012-02-27 19:26:20.921 Keyboard[36856:207] Dealloc Splatter
2012-02-27 19:26:23.884 Keyboard[36856:207] *** -[CanvasView release]: message sent to deallocated instance 0x5c2f020
2012-02-27 19:26:28.365 Keyboard[36856:207] *** NSInvocation: warning: object 0x5c2f020 of class '_NSZombie_CanvasView' does not implement methodSignatureForSelector: -- trouble ahead
2012-02-27 19:26:28.365 Keyboard[36856:207] *** NSInvocation: warning: object 0x5c2f020 of class '_NSZombie_CanvasView' does not implement doesNotRecognizeSelector: -- abort

这是它的回溯

#0  0x015f8709 in ___forwarding___ ()
#1  0x015f8522 in __forwarding_prep_0___ ()
#2  0x000062db in -[MenuViewController setCurrentView:] (self=0xab0aad0, _cmd=0x415f2, currentView=0x5c1f9e0) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/MenuViewController.m:21
#3  0x00004ee9 in -[MenuViewController launchVisualizer:] (self=0xab0aad0, _cmd=0x41517, sender=0x0) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/MenuViewController.m:180
#4  0x00d6befc in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] ()
#5  0x00d7e506 in -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] ()
#6  0x0000423a in -[MenuViewController switchViews:] (self=0xab0aad0, _cmd=0x415d3, number=0x5d2a960) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/MenuViewController.m:70
#7  0x00d6c94e in __NSThreadPerformPerform ()
#8  0x016688ff in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#9  0x015c688b in __CFRunLoopDoSources0 ()
#10 0x015c5d86 in __CFRunLoopRun ()
#11 0x015c5840 in CFRunLoopRunSpecific ()
#12 0x015c5761 in CFRunLoopRunInMode ()
#13 0x021961c4 in GSEventRunModal ()
#14 0x02196289 in GSEventRun ()
#15 0x005f3c93 in UIApplicationMain ()
#16 0x00002890 in main (argc=1, argv=0xbfffec98) at /Users/tjfallon/Documents/iOS Projects/Dropbox/Working Directory/Keyboard/Keyboard/main.m:16

最佳答案

您不应该在 dealloc 中调用 removeFromSuperview。如果 UIKit 这样做,那是它的事,我相信 Apple 知道它在做什么,但在你的 dealloc 中,这是胡说八道。如果你目前是其他人的 subview ,你永远不应该被释放(因为他们正在保留你)。所以充其量这不会做任何事情,而在最坏的情况下它会破坏事情。

接下来,关于这个:

As I understand from scouring the docs and SO, an unsafe_unretained property should nil itself on dealloc, just like the weak reference that isn't compatible with iOS 4?

这是不正确的。它被称为“不安全”的原因是它本身不是 nil。当底层对象被释放时,它什么也不做。这就是让他们不安全的原因。尽可能避免使用它们。

首先,以这种方式声明您的 currentView 属性:

@property(nonatomic, readwrite, strong) UIView* currentView;

其次,为您的属性使用访问器(self.currentView,而不是currentView)。在这种情况下,这不会引起您的问题,但这是一个好习惯,可以让您避免其他麻烦。

最后,崩溃看起来像是在 Splatterdealloc 中。你在那做什么?

顺便说一句,dealloc 在 ARC 中有点不寻常。它对于从 NSNotificationCenter 或 KVO 中删除自己很有用,或者对于释放 malloc 内存很有用,但在 View 类中通常不需要它。

关于objective-c - 仅在 iOS 4 上的 ARC 中为 "Modifying layer that is being finalized",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9473127/

相关文章:

ios - 如何从 SQLite 数据库中检索值

ios - 由于未捕获的异常 'NSInvalidArgumentException' 而终止应用程序,原因 : '-[__NSCFString size]: unrecognized selector sent to instance

ios - 在 UINavigationBar 顶部添加 UIView 时如何重叠状态栏?

ios - 我如何用新项目重新加载 TTLaucherView?

ios - 如何检查用户是否从 iOS 上的另一个应用程序调用我的应用程序?

ios - Master Detail View 我想回到主视图而不是详细 View

Swift,字典解析错误

objective-c - UIScrollView 仅在底部禁用垂直弹跳

objective-c - 如果未链接到 XCode 或 Instrument,应用程序会崩溃

ios - NSURLSession错误中返回值