如果在下载完成之前,我的视图控制器被用户关闭,则会出现此问题.我怀疑发生了什么,一旦我的视图控制器被解雇,回调块是唯一保留对控制器的强烈引用.回调块仅保留在后台线程中,因此一旦释放,所有在回调块范围内捕获的对象也将被释放,尽管在后台队列中.
这是一个问题:在后台队列中释放导致dealloc在同一个队列中运行,而不是主队列.这又在后台调用了dealloc并且应用程序崩溃:
2012-01-19 12:47:36.349 500px iOS[4892:12107] bool _WebTryThreadLock(bool),0x306c10: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now... [Switching to process 16643 thread 0x4103] [Switching to process 16643 thread 0x4103] (gdb) where #0 0x307fd3c8 in _WebTryThreadLock () #1 0x307ff1b0 in WebThreadLock () #2 0x33f7865e in -[UITextView dealloc] () #3 0x0005d2ce in -[GCPlaceholderTextView dealloc] (self=0x309010,_cmd=0x340ce6b8) at /Users/ash/DropBox/500px/500px-ios/500px iOS/500px iOS/GCPlaceholderTextView.m:113 #4 0x337cac42 in -[NSObject(NSObject) release] () #5 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview] () #6 0x33e836cc in -[UIScrollView removeFromSuperview] () #7 0x33f762f0 in -[UITextView removeFromSuperview] () #8 0x33e01de2 in -[UIView dealloc] () #9 0x337cac42 in -[NSObject(NSObject) release] () #10 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview] () #11 0x33e01de2 in -[UIView dealloc] () #12 0x33f437e4 in -[UIScrollView dealloc] () #13 0x337cac42 in -[NSObject(NSObject) release] () #14 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview] () #15 0x33e836cc in -[UIScrollView removeFromSuperview] () #16 0x33e01de2 in -[UIView dealloc] () #17 0x337cac42 in -[NSObject(NSObject) release] () #18 0x33dee5f4 in -[UIView(Hierarchy) removeFromSuperview] () #19 0x33e01de2 in -[UIView dealloc] () #20 0x337cac42 in -[NSObject(NSObject) release] () #21 0x33e5a00e in -[UIViewController dealloc] () #22 0x00035f16 in -[PXPhotoViewController dealloc] (self=0x5158d0,_cmd=0x340ce6b8) at /Users/ash/DropBox/500px/500px-ios/500px iOS/500px iOS/PXPhotoViewController.m:118 #23 0x337cac42 in -[NSObject(NSObject) release] () #24 0x337e5046 in sendRelease () #25 0x331fc92e in _Block_object_dispose () #26 0x0003c33a in __destroy_helper_block_ () at /Users/ash/DropBox/500px/500px-ios/500px iOS/500px iOS/PXPhotoViewController.m:878 #27 0x331fc88e in _Block_release () #28 0x331fc91c in _Block_object_dispose () #29 0x000c8d32 in __destroy_helper_block_ () at /Users/ash/DropBox/500px/500px-ios/500px iOS/500px iOS/PXPhotoFetcher.m:557 #30 0x331fc88e in _Block_release () #31 0x35eec8ec in _dispatch_call_block_and_release () #32 0x35ee2de2 in _dispatch_queue_drain () #33 0x35ee2f32 in _dispatch_queue_invoke () #34 0x35ee24f2 in _dispatch_worker_thread2 () #35 0x34ecb590 in _pthread_wqthread () #36 0x34ecbbc4 in start_wqthread ()
我在主线程上执行的代码如下所示:
[[PXPhotoFetcher sharedPXPhotoFetcher] fetchPhotoDetailsWithPriority:PhotoRequestLowPriority withCallback:^(PXPhotoModel *thePhotoModel) { // a callback function which captures self in its scope } forModel:model];
我正在构建4.3,所以如果我在回调块中使用__unsafe_un自己的引用,这将修复我当前的问题,但是引入一个悬挂指针的新问题.
建议为此构建解决方案是什么?其他解决方法包括overriding release
,以便它始终在主线程上调用,但这显然不会在ARC环境中工作.
这似乎应该是ARC和GCD的一个更常见的问题,但是我在网上找不到任何东西.这只是因为我的目标是iOS 5并不能使用弱引用?
解决方法
这个问题是由于在后台被块破坏引起的,所以我把它放到一个局部变量中,并且在一个后台块内调用它,然后将它传递给主线程上的一个块,这样它就被释放出来了.它也很杂乱,但如下所示:
void(^block)(void) = ^{/*do all the things*/}; dispatch_async(queue,^{ block(); dispatch_async(dispatch_get_main_queue(),^{ if ([block isKindOfClass:[NSString class]]) NSLog(@"Whoa,bro"); }); });
主线程上的代码只是一个技巧,以确保编译器不仅仅是优化掉代码;我需要最后由主线程释放块对象.该代码似乎适用于-Os编译器优化级别.
所以我提出了一个解决我的问题,虽然它是超级黑客.自从修复以来,我一直无法重现这个问题,尽管这是一个非常糟糕的建筑设计.
要回顾一下,问题是我在后台队列中有一个被破坏的块.该块是一个对象,它拥有强大的对回调块的引用,该块包含对self的强引用.背景块正在从后台队列中释放出来.所以我所做的是将呼叫转到另一个调度调用中,到主队列.所以我的提取方法是从这个:
dispatch_async(backgroundQueue,^{ /* do all the things */ });
为此:
dispatch_async(dispatch_get_main_queue(),^{ dispatch_async(backgroundQueue,^{ /* do all the things */ }); });
代码异步地将块分派到主队列,然后将块分派到后台队列.由于后台程序块是一个对象,属于主队列的范围,它在主线程上被释放,导致UITextView的最终取消分配导致主队列中发生崩溃,同时解决了我的问题.