CLLocationManagerDelegate のメソッド内で CLLocationManager インスタンスを release すると EXC_BAD_ACCESS で落ちる
CLLocationManagerDelegate の didUpdateToLocation メソッド内で CLLocationManager のインスタンスを release すると EXC_BAD_ACCESS が発生してアプリが落ちるという現象にハマったのでその対処方法をまとめます。
@interface HogeController : UIViewController <CLLocationManagerDelegate> { CLLocationManager *locationManager; } - (IBAction)respondToButtonClick:(id)sender; @end @implementation HogeController - (IBAction)respondToButtonClick:(id)sender { locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; [locationManager startUpdatingLocation]; } #pragma mark CLLocationManagerDelegate - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { : : 処理 : [locationManager stopUpdatingLocation]; locationManager.delegate = nil; // EXC_BAD_ACCESS で落ちる [locationManager release], locationManager = nil; } @end
クラッシュログはこのようになっていました。CLLocationManager onClientEventLocation: という見慣れないメッソド表示がありました。
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0xffffffff Crashed Thread: 0 Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 libobjc.A.dylib 0x34499c96 objc_msgSend + 14 1 CoreLocation 0x34457c48 -[CLLocationManager onClientEventLocation:] + 808 2 CoreLocation 0x34457f42 -[CLLocationManager onClientEvent:supportInfo:] + 46 3 CoreLocation 0x34455a64 OnClientEvent + 16 4 CoreLocation 0x3445178a CLClientInvokeCallback(__CLClient*, CLClientEvent, __CFDictionary const*) + 46 5 CoreLocation 0x34453c40 CLClientHandleDaemonDataLocation(__CLClient*, CLClientLocation const*, __CFDictionary const*) + 208 6 CoreLocation 0x34453d96 CLClientHandleDaemonData(__CFMessagePort*, long, __CFData const*, void*) + 290 7 CoreFoundation 0x3094e706 __CFMessagePortPerform + 242 8 CoreFoundation 0x30957a90 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 20 9 CoreFoundation 0x30959838 __CFRunLoopDoSource1 + 160 10 CoreFoundation 0x3095a606 __CFRunLoopRun + 514 11 CoreFoundation 0x308eaebc CFRunLoopRunSpecific + 224 12 CoreFoundation 0x308eadc4 CFRunLoopRunInMode + 52 13 GraphicsServices 0x30269418 GSEventRunModal + 108 14 GraphicsServices 0x302694c4 GSEventRun + 56 15 UIKit 0x30a10d62 -[UIApplication _run] + 398 16 UIKit 0x30a0e800 UIApplicationMain + 664 17 Sample 0x0000233a main (main.m:14) 18 Sample 0x00002304 start + 32
メインスレッドでアプリが落ちているのがわかります。これだけ見ても何が原因かさっぱりわかりませんでしたが、didUpdateToLocation メソッドはメインスレッドで実行されるので、このメソッド内の処理が怪しいことだけは確実です。
対処方法その1
以下のようにデリゲートの無効化とインスタンスのリリース処理をメソッドに切り出して performSelector で呼び出してやると落ちずに動きました。
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { : : 処理 : [locationManager stopUpdatingLocation]; [self performSelector:@selector(discardLocationManager) onThread:[NSThread currentThread] withObject:nil waitUntilDone:NO]; } - (void)discardLocationManager { locationManager.delegate = nil; [locationManager release], locationManager = nil; }
didUpdateToLocation メソッドの処理を終わらせてからリリースしないと落ちるようです。
対処方法その2
NSZombieEnabled を YES に設定すると落ちません。かなり謎です。普通は EXC_BAD_ACCESS で落ちる場面で実行が止まってエラーログが吐き出されるのですが、NSZombieEnabled を YES に設定すると何事もなかったかのように動きます。メッセージすら表示されません。
NSZombieEnabled の設定方法は以下を参考にしてください。