Недокументированные изменения API в CoreBluetooth

#swift #objective-c #xcode #core-bluetooth

#swift #objective-c #xcode #ядро-bluetooth

Вопрос:

В Xcode 12.3 CoreBluetooth.CBService.peripheral определяется в objective-c как:

 @property(assign, readonly, nonatomic) CBPeripheral *peripheral;
 

Обновление: вот быстрый перевод приведенного выше в Xcode 12.3:

 unowned(unsafe) open var peripheral: CBPeripheral { get }
 

В Xcode 13.0 CBService.peripheral определяется в swift как:

 weak var peripheral: CBPeripheral? { get }
 

В документации Apple указано, что этот API существует с iOS5, и никаких изменений не было. Однако в Xcode 13 переменная явно является необязательной. (И это не является необязательным в Xcode 12.3, поскольку в нем отсутствует атрибут nullable .)

Исправление относительно простое (например service.peripheral -> service?.peripheral ), но оно делает невозможным использование одного и того же кода как для Xcode 12.3, так и для 13.0. Мне интересно, есть ли здесь какой-то нюанс, который я упускаю?

Ответ №1:

Опции являются неотъемлемой частью Swift и не являются частью Objective-C. Ненулевая ссылка в Swift гарантированно имеет значение, в то время как любая ссылка в Objective C теоретически может иметь null значение .

Декораторы nullable и nonnull были введены для улучшения взаимодействия со Swift с тем побочным эффектом, что они также более четко документируют API Objective C.

Core Bluetooth — это Objective C API, и, как вы заметили, он доступен с iOS5; Задолго до Swift и nullable декоратора.

Итак, дело не столько в том, что API изменился, скорее вы сравниваете Objective C API с Swift API, а Apple не добавила nullable декоратор в основной Bluetooth API.

Ни один из этих API не изменился; Объявление Swift для peripheral on CBService всегда было необязательным. Объявление Objective-C никогда не имело, nullable но null значение всегда было возможно.

Добавление nullable не изменит поведение API, только его объявление в Objective C. Это потенциально может стать критическим изменением для кода Objective-C или, по крайней мере, может привести к сбою компиляции, поэтому у Apple нет причин изменять его, и есть веская причина не делать этого.

Обновить

Судя по вашему комментарию, действительно, похоже, что в объявлении Swift CBService.peripheral from unowned(unsafe) произошло существенное изменение на слабый необязательный параметр.

Это гораздо более безопасное объявление, поскольку в предыдущем определении на вас лежала обязанность проверить, что peripheral это не nil так, и если вы этого не сделали, произошел потенциальный сбой.

Я не думаю, что за многие годы программирования с Core Bluetooth мне когда-либо приходилось использовать CBService.peripheral , однако вы можете использовать условную компиляцию на основе версии Swift для написания кода, который работает как в Xcode 13, так и в более ранних версиях:

 var p: CBPeripheral?
#if swift(<5.5) 
    if s.peripheral != nil {
        p = s.peripheral
    }
#else
        p = s.peripheral
#endif
 

Комментарии:

1. Спасибо за ваш подробный ответ! Мне удалось найти разницу. Xcode12.3 unowned(unsafe) open var peripheral: CBPeripheral { get } и Xcode 13: weak var peripheral: CBPeripheral? { get } — таким образом, objc, возможно, не изменился, но быстрое преобразование изменилось.

2. С учетом этих различий, есть ли способ скомпилировать один и тот же код CoreBluetooth swift как для Xcode 12.3, так и для Xcode 13?