当我的代码是这样编写时:
在button出现touch操作时,button的action会执行下面的代码,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *leftId = @"face_id"; NSString *rightId = @"face_id"; Result *compareResult = [ResouceFactory CompareTwoPic:leftId pic2:rightId];//会出现网络上传数据以及返回一个Result对象 NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:compareResult,kGETCOMPARERESULT, nil]; [NOTE_CENTER postNotificationName:kGETCOMPARERESULT object:dict];// post一个通知,并且传入返回的Result对象 });
这段代码会产生一个网络交互,需要上传一些数据,并且返回一个数据对象,该数据对象将会在另外一个UIViewController中使用。获得数据对象之后,发出通知。下面的代码就是接收到通知之后执行的操作:
-(void)processResult:(id)re { [self stopActivity]; Result *result = [(NSDictionary*)re objectForKey:kGETRESULT]; ResultViewController *resultViewController = [[ResultViewController alloc] initWithNibName:nil bundle:nil]; resultViewController.result = result; [resultViewController showResult]; }
执行上面的代码时,会出现exc_bad_excess错误,通过设置Enable Zombie Object(Product->Scheme->Edit Scheme,然后选择Diagnostics,给Enable Zombie Object勾上),控制台输出如下内容
bool _WebTryThreadLock(bool), 0x1a3c4930: 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... 1 0x364606fb WebThreadLock 2 0x30a106a3 <redacted> 3 0x30a10583 <redacted> 4 0x1d24fc -[GADWebView initWithFrame:] 5 0x1b0a4c -[GADSlot initWithFrame:] 6 0x1b0e88 -[GADSlot initWithView:] 7 0x1ae6c4 -[GADBannerView commonInitializer] 8 0x1aee9c -[GADBannerView initWithAdSize:] 9 0xeebe1 -[BaseViewController initgAdBanner] 10 0xee7bd -[BaseViewController loadView] 11 0x307f730d <redacted> 12 0x307f7289 <redacted> 13 0xf7eff -[ResultViewController showResult] 14 0x11c4e3 -[PicChooseViewController processCompareResult:] 15 0x11ab83 -[PicChooseViewController receiveNotification:] 16 0x2df991f1 <redacted> 17 0x2df0d57f _CFXNotificationPost 18 0x2e8f7a3d <redacted> 19 0x2e8fc31b <redacted> 20 0x11cce9 __35-[PicChooseViewController compare:]_block_invoke 21 0x3905d833 <redacted> 22 0x39064ad7 <redacted> 23 0x39064d29 <redacted> 24 0x3919fbd3 _pthread_wqthread 25 0x3919fa98 start_wqthread
原来是我的ResultViewController中loadView时需要分配GADBannerView,从而出现了EXC_BAD_ACCESS异常。但是我将GADBannerView删除还是会出现一样的错误,只是由GADBannerView转移到了另外一个View的alloc方法上。
经过几番周折,终于知道了原因是:dispatch_async的调用,导致线程出现的交换,程序由主线程换到了其他线程上。而UI线程必须在主线程上操作。所以现在可以用下面的方法来解决:
1. 将dispatch_async改为dispatch_sync,即仍然在主线程上进行网络交换,但这不是我想要的;
2. 将processResult方法中的操作改到主线程上,可以用
performSelectorOnMainThread:
withObject:
waitUntilDone方法,例如
[self performSelectorOnMainThread:@selector(processResult:) withObject:note.object waitUntilDone:YES];
采用上面的方法后,基本上解决了问题。
反思:
1. UI线程一定要在主线程上,否则会出现EXC_BAD_ACCESS异常。一般来说EXC_BAD_ACCESS异常是使用释放了对象。而UI线程里出现EXC_BAD_ACCESS异常,一般是在UIView调用alloc方法时,因此常常会导致开发者莫名其貌;
2. 在什么类型线程发出通知,那么接收通知的处理也是在什么类型线程。