-->

EXC_BAD_ACCESS when setting a CLBeacon to nil

2019-07-28 04:26发布

问题:

The following will perform a crash when setting CLBeacon to nil.

CLBeacon *beacon = [[CLBeacon alloc] init];
beacon = nil; // crash

Is it not possible to deallocate an initialized CLBeacon?


This can be reproduced by simply adding the code above to a fresh project inside the App Delegate's didFinishLaunchingWithOptions

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    CLBeacon *beacon = [[CLBeacon alloc] init];
    beacon = nil; // crash
    return YES;
}

回答1:

The apple documentation for CLBeacon states:

You do not create instances of this class directly. The location manager object reports encountered beacons to its associated delegate object.

The reason it crashes is an implementation detail that doesn't really matter, but it is due to the fact that CLBeacons are not properly initialized when you just call init. When it deallocates, CLBeacon dereferences it's _internal ivar and crashes if it is NULL.

You can see this by looking at the value of the CLBeacon->_internal ivar in the debugger. If you create the beacon using init then the ivar is NULL, but if you create it with [[CLBeacon alloc] initWithCoder:nil] it will have a value and it doesn't crash when you set the beacon to nil.



回答2:

Ran into this problem while using a mocked subclass. My tests would crash every time a mocked subclass was dealloced by ARC.

Solution is to call the correct init method on CLBeacon. Looking here we see that there is an addition init method. Declare it in a category in your code.

@interface CLBeacon (PRXInternal)

- (id)initWithProximityUUID:(id)arg1 major:(id)arg2 minor:(id)arg3 proximity:(long long)arg4 accuracy:(double)arg5 rssi:(long long)arg6 ;

@end

Call this initializer if you need an instance of the class. Do not include in production code.