Sharing passion in Swift

#2 Un-crash swiftly from CBCentralManager callbacks to a deallocated object

CBCentralManager

CBCentralManager is a class from CoreBluetooth framework for Bluetooth connectivity. It servers as a configurator of communication with external devices, represented as CBPeripheral objects by the framework. Configuration of Bluetooth interface and communication with peripherals is done asynchronuousley, so one can set self as delegate in order to catch Bluetooth-related events.

Credits: Apple Inc., Core Bluetooth Programming Guide

CBCentralManagerDelegate and the problem

To receive callbacks from CBCentralManager instance its delegate has to implement CBCentralManagerDelegate protocol methods. The only required method is centralManagerDidUpdateState:, which tells us about Bluetooth support of the device the app is run on.

Problem

Aforementioned method can be called eventhough delegate object became deallocated. Let’s see an example of that.

Suppose we had a class Object that is initialised with an instant of CBCentralManager class. During initialisation the Object instance sets itself as CBCentralManager‘s delegate.

1

2

3

4

5

6

7

8

9

10

classObject{

letcentralManager:CBCentralManager

init(centralManager:CBCentralManager){

self.centralManager=centralManager

self.centralManager.delegate=self

}

}

We implement required CBCentralManagerDelegate protocol methods in order to become a CBCentralManager‘s delegate:

1

2

3

4

5

6

extensionObject: CBCentralManagerDelegate{

@objc funccentralManagerDidUpdateState(central:CBCentralManager){}

}

One remark – CBCentralManagerDelegate protocol requires conforming to NSObjectProtocol as well. The simplest way to do that is to inherit from NSObject class:

1

2

3

4

5

6

7

8

9

10

11

classObject: NSObject{

//...

init(centralManager:CBCentralManager){

self.centralManager=centralManager

super.init()

self.centralManager.delegate=self

}

}

Imagine we had a function in which an instance of our Object was created but wasn’t stored anywhere else.

1

2

3

4

5

6

7

funcfoo(){

letcentralManager=CBCentralManager(delegate:nil,queue:nil)

let_=Object(centralManager:centralManager)

}

After function call app would crash with exception of type Unrecognised selector sent to instance, since CBCentralManager would try to send messages to its delegate, despite it would be deallocated immediately after returning from the function.

Fix and un-crash from doom!

There is however a quick fix for such a problem. We could use deinit method of our Object class to nullify centralManager’s delegate. After deallocation of Object instance messages from centralManager won’t be sent. Hurray :)!