diff --git a/device/bluetooth/bluetooth_socket_mac.mm b/device/bluetooth/bluetooth_socket_mac.mm index 2348f81a49b34..cff017dd36e16 100644 --- a/device/bluetooth/bluetooth_socket_mac.mm +++ b/device/bluetooth/bluetooth_socket_mac.mm @@ -53,6 +53,12 @@ using device::BluetoothSocket; // The device being queried. IOBluetoothDevice* __weak _device; + + // While the SDP query is outstanding, the listener holds a strong reference + // to itself so that it outlives any late -sdpQueryComplete:status: dispatch + // from IOBluetooth, which stores the performSDPQuery: target unretained. + // This is a workaround for a macOS bug, see Apple Feedback report FB13705522. + SDPQueryListener* __strong _strongSelf; } - (instancetype)initWithSocket:(scoped_refptr)socket @@ -77,6 +83,8 @@ using device::BluetoothSocket; _device = device; _success_callback = std::move(success_callback); _error_callback = std::move(error_callback); + // Retain self until IOBluetooth delivers -sdpQueryComplete:status:. + _strongSelf = self; } return self; @@ -92,12 +100,18 @@ using device::BluetoothSocket; - (void)sdpQueryComplete:(IOBluetoothDevice*)device status:(IOReturn)status { DCHECK_EQ(device, _device); + // IOBluetooth has called back; drop the self-retain. Keep |self| alive for + // the remainder of this method via a local strong reference. + SDPQueryListener* __strong strongSelf = _strongSelf; + _strongSelf = nil; if (!_error_callback) { // This can happen when the target is called after SDP query timeout. + (void)strongSelf; return; } _socket->OnSDPQueryComplete(status, device, std::move(_success_callback), std::move(_error_callback)); + (void)strongSelf; } - (BluetoothSocket::ErrorCompletionCallback)takeErrorCallback {