diff --git a/device/bluetooth/bluetooth_discovery_manager_mac.mm b/device/bluetooth/bluetooth_discovery_manager_mac.mm index bccb75c98b137e..7a8dc3e4777938 100644 --- a/device/bluetooth/bluetooth_discovery_manager_mac.mm +++ b/device/bluetooth/bluetooth_discovery_manager_mac.mm @@ -25,6 +25,7 @@ class BluetoothDiscoveryManagerMacClassic; - (instancetype)initWithManager: (device::BluetoothDiscoveryManagerMacClassic*)manager; +- (void)resetManager; @end @@ -48,10 +49,21 @@ class BluetoothDiscoveryManagerMacClassic const BluetoothDiscoveryManagerMacClassic&) = delete; ~BluetoothDiscoveryManagerMacClassic() override { + [inquiry_ stop]; // IOBluetoothDeviceInquiry's delegate property is configured as "assign" // rather than "weak". If it is not manually reset then our delegate could be // accessed after we drop our strong reference and the object is freed. inquiry_.delegate = nil; + // Clear the back-pointer so any kept-alive callback becomes a no-op + // instead of dereferencing this freed C++ object. + [inquiry_delegate_ resetManager]; + // Keep `inquiry_delegate_` alive across one main-run-loop turn so any + // already-enqueued IOBluetooth callbacks (which captured the assign-held + // delegate id at enqueue time) hit a live receiver. See FB13705522. + BluetoothDeviceInquiryDelegate* __strong delegate = inquiry_delegate_; + dispatch_async(dispatch_get_main_queue(), ^{ + (void)delegate; + }); } // BluetoothDiscoveryManagerMac override. @@ -213,19 +225,29 @@ BluetoothDiscoveryManagerMac* BluetoothDiscoveryManagerMac::CreateClassic( return self; } +- (void)resetManager { + _manager = nullptr; +} + - (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender { - _manager->DeviceInquiryStarted(sender); + if (_manager) { + _manager->DeviceInquiryStarted(sender); + } } - (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender device:(IOBluetoothDevice*)device { - _manager->DeviceFound(sender, device); + if (_manager) { + _manager->DeviceFound(sender, device); + } } - (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender error:(IOReturn)error aborted:(BOOL)aborted { - _manager->DeviceInquiryComplete(sender, error, aborted); + if (_manager) { + _manager->DeviceInquiryComplete(sender, error, aborted); + } } @end