2

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;
}
Atticus
  • 6,585
  • 10
  • 35
  • 57
  • 1
    You aren't supposed to be create any instances of `CLBeacon`. It crashes because it doesn't get properly initialized unless you call `initWithCoder:`. – dan Jan 05 '17 at 17:10
  • @dan It inherits from `NSObject`, so why is this? – shallowThought Jan 05 '17 at 17:12
  • @shallowThought Which part are you asking about? You aren't supposed to create any instances of it because it says in the documentation not to. It doesn't get properly initialized unless you call `initWithCoder:` because the code that properly initializes it is in `initWithCoder:`. – dan Jan 05 '17 at 17:14
  • Thanks guys - it does clearly state this in the docs.. Sick. RTFM harder. – Atticus Jan 05 '17 at 17:18
  • 1
    I suspect that if you get an instance of `CLBeacon` as a result of a ranging callback, assign it to a variable, and then assign nil to that variable, then this crash will not happen. You can call the crash a "bug" if you wish, but Apple would probably say that if you are using the class in a way it is designed not to be used, they don't care about the bug. – davidgyoung Jan 05 '17 at 17:18
  • @dan In my understanding: `[[CLBeacon alloc] init]` does return a pointer to valid memory address and thus should be `nil`-able. davidgyoungs comment makes sense rationally but I do not get it technically. – shallowThought Jan 05 '17 at 17:20
  • 1
    @shallowThought `CLBeacon` dereferences an internal pointer in its `dealloc` method and if you create it using `init` that internal pointer is `NULL` which causes the crash. – dan Jan 05 '17 at 17:22
  • @dan would you mind providing an Answer to this question regarding improper usage of the class? I'd like give credit for the proper answer, rather than self-answering. – Atticus Jan 05 '17 at 17:22
  • @dan that does make sense. – shallowThought Jan 05 '17 at 17:23
  • note: In Swift 2 / Xcode 7 you could instantiate CLBeacon() instances and print them. In Swift 3 / Xcode 8 printing will cause an EXC_BAD_ACCESS. – wyu Jun 08 '17 at 18:17

2 Answers2

3

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.

dan
  • 9,695
  • 1
  • 42
  • 40
1

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.

hhanesand
  • 990
  • 11
  • 28