最近在iOS4.3.3的机器上发现用asi的block会有很大概率产生crash.
用了ARC
crash表现情况为:bool _WebTryThreadLock(bool), 0x20a0fb20: 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...
2 libsystem_c.dylib 0x3388b727 _sigtramp + 34; 3 WebCore 0x31d363bf _ZL17_WebTryThreadLockb + 102; 4 WebCore 0x31d381b1 WebThreadLock + 52; 5 UIKit 0x3317265f -[UITextView dealloc] + 30; 6 CoreFoundation 0x334afc43 -[NSObject(NSObject) release] + 30; 7 snstaoban 0x00190ffb __arclite_objc_storeStrong + 54; 8 snstaoban 0x0001e5ab -[ShareInputView .cxx_destruct] + 106; 9 libobjc.A.dylib 0x3398d961 object_cxxDestructFromClass + 52; 10 libobjc.A.dylib 0x3398fb15 object_cxxDestruct + 12; 11 libobjc.A.dylib 0x3398fb25 objc_destructInstance + 12; 12 libobjc.A.dylib 0x3398d917 object_dispose + 26; 13 CoreFoundation 0x334afee5 -[NSObject(NSObject) dealloc] + 24; 14 UIKit 0x32ffbec7 -[UIView dealloc] + 382; 15 UIKit 0x33058739 -[UIImageView dealloc] + 64; 16 snstaoban 0x0001d485 -[ShareInputView dealloc] + 120; 17 CoreFoundation 0x334afc43 -[NSObject(NSObject) release] + 30; 18 UIKit 0x32fe85f5 -[UIView(Hierarchy) removeFromSuperview] + 364; 19 UIKit 0x32ffbde3 -[UIView dealloc] + 154; 20 CoreFoundation 0x334afc43 -[NSObject(NSObject) release] + 30; 21 UIKit 0x3305400f -[UIViewController dealloc] + 174; 22 snstaoban 0x00070467 -[RootViewController dealloc] + 238; 23 snstaoban 0x00015f21 -[ShareSetViewController dealloc] + 120; 24 CoreFoundation 0x334afc43 -[NSObject(NSObject) release] + 30; 25 snstaoban 0x00190f29 __arclite_objc_release + 20; 26 snstaoban 0x00178723 __destroy_helper_block_160 + 10; 27 libsystem_blocks.dylib 0x365a688f _Block_release + 58; 28 CoreFoundation 0x334e4285 -[__NSMallocBlock release] + 8; 29 snstaoban 0x00190f29 __arclite_objc_release + 20; 30 snstaoban 0x000733a9 __destroy_helper_block_64 + 12; 31 libsystem_blocks.dylib 0x365a688f _Block_release + 58; 32 CoreFoundation 0x334e4285 -[__NSMallocBlock release] + 8; 33 CoreFoundation 0x334b01a1 CFRelease + 68; 34 CoreFoundation 0x334b5ba9 -[__NSArrayM dealloc] + 92; 35 CoreFoundation 0x334afc43 -[NSObject(NSObject) release] + 30; 36 CoreFoundation 0x334b01a1 CFRelease + 68; 37 CoreFoundation 0x334b2ebb _CFAutoreleasePoolPop + 146; 38 libdispatch.dylib 0x30df06a5 _dispatch_worker_thread2 + 372; 39 libsystem_c.dylib 0x33881591 _pthread_wqthread + 264; 40 libsystem_c.dylib 0x33881bc4 _init_cpu_capabilities + 4294967295||dep=58,idx=57
原因是asi的completionBlock和failureBlock在非主线程(Thread 1 name: Dispatch queue: com.apple.root.low-priority)被释放,然后controller, view接着在这个线程被释放导致crash.
测试了5.0一上也是在非主线程被释放, 但是不会导致crash.
但是4.3.3的模拟器block是在主线程被释放.
于是查看asi的代码发现一个trick.
在dealloc里有
#if NS_BLOCKS_AVAILABLE [self releaseBlocksOnMainThread]; #endif #if NS_BLOCKS_AVAILABLE - (void)releaseBlocksOnMainThread { NSMutableArray *blocks = [NSMutableArray array]; if (completionBlock) { [blocks addObject:completionBlock]; [completionBlock release]; completionBlock = nil; } if (failureBlock) { [blocks addObject:failureBlock]; [failureBlock release]; failureBlock = nil; } .... [[self class] performSelectorOnMainThread:@selector(releaseBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]]; } // Always called on main thread + (void)releaseBlocks:(NSArray *)blocks { // Blocks will be released when this method exits } #endif
疑问:
1.blocks变量应该在主线程被释放还是releaseBlocksOnMainThread所在线程?有文档能证明么?
2.releaseBlocks为什么里面什么都不干呢?
另外:我测试解决4.3.3的blockcrash问题就是改掉
+ (void)releaseBlocks:(NSMutableArray *)blocks { // Blocks will be released when this method exits [blocks removeAllObjects];//解决4.3.3子线程释放block的bug
}
不知道这样做是否合理, 如果是合理的为什么作者不这样做呢?
天蓬老师2017-04-17 11:32:01
首先,你改了这个方法原理上并不能解决blocks变量被过度释放的问题,因为你只是对blocks这数组的元素做了操作。
另,回答你两个问题:
1,blocks变量一定是在主线程释放的,就是避免多线程并发时的冲突,所以才会
[[self class] performSelectorOnMainThread:@selector(releaseBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]];
2,我还真没想到为什么他是空的,我再研究一下~~~
另外,建议你把创建请求,处理block的代码都贴上来,因为现在看现象我觉得是你处理Block的时候有问题,不是ASI的问题
补充高人点解,弄懂了为什么这个方法是空的。因为blocks是作为参数传给releaseBlocks:的,所以引用计数会+1,当函数执行结束,blocks引用计数-1,所以被释放