search

Home  >  Q&A  >  body text

ios - ASIHTTPRequest block释放问题

最近在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

}
不知道这样做是否合理, 如果是合理的为什么作者不这样做呢?

PHP中文网PHP中文网2773 days ago773

reply all(1)I'll reply

  • 天蓬老师

    天蓬老师2017-04-17 11:32:01

    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

    [[self class] performSelectorOnMainThread:@selector(releaseBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]];

    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

    Supplementary

    Experts’ 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

    reply
    0
  • Cancelreply