EXC_BAD_ACCESS异常 调试过程

当我的代码是这样编写时:

在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 
3   0x30a10583 
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 
12  0x307f7289 
13  0xf7eff -[ResultViewController showResult]
14  0x11c4e3 -[PicChooseViewController processCompareResult:]
15  0x11ab83 -[PicChooseViewController receiveNotification:]
16  0x2df991f1 
17  0x2df0d57f _CFXNotificationPost
18  0x2e8f7a3d 
19  0x2e8fc31b 
20  0x11cce9 __35-[PicChooseViewController compare:]_block_invoke
21  0x3905d833 
22  0x39064ad7 
23  0x39064d29 
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. 在什么类型线程发出通知,那么接收通知的处理也是在什么类型线程。


你可能感兴趣的:(Objective-C,IOS)