最近在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
}
不知道这样做是否合理, 如果是合理的为什么作者不这样做呢?
First of all, your change to this method cannot solve the problem of excessive release of the blocks variable in principle, because you only operate on the elements of the blocks array.
Also, to answer your two questions:
1. The blocks variable must be released in the main thread to avoid conflicts during multi-thread concurrency, so
2. I really didn’t expect why it was empty. I’ll do more research~~~
In addition, I suggest you post the code to create the request and process the block, because looking at the phenomenon now, I think there is a problem when you process the block, not the ASI problem
SupplementaryExperts’ advice, I understand why this method is empty. Because blocks are passed as parameters to releaseBlocks:, the reference count will be +1. When the function execution ends, the blocks reference count -1, so it will be released