2015-09-11 2 views
2

Согласно pre-release Swift 2 documentation есть теперь #available ключевое слово, которое может быть использовано с помощью оператора if let или guard для проверки наличия API. Это приводит следующий пример:Swift #available ключевого слова против respondsToSelector

let locationManager = CLLocationManager() 
if #available(iOS 8.0, OSX 10.10, *) { 
    locationManager.requestWhenInUseAuthorization() 
} 

Или

let locationManager = CLLocationManager() 
guard #available(iOS 8.0, OSX 10.10, *) else { return } 
locationManager.requestWhenInUseAuthorization() 

Какой он (документ) утверждает, является эквивалентом следующего кода Objective-C:

if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)]) { 
    // Method is available for use. 
} else { 
    // Method is not available. 
} 

Очевидно respondsToSelector: работает только на подклассах NSObject, тогда как ключевое слово #available будет работать даже для «чистого» кода Swift, поэтому я ценю это преимущество.

Однако, начиная с разработки iOS, мне всегда приходилось полагать, что лучшей практикой для этой ситуации является обнаружение присутствия API, а не полагаться на версию, которую она представила.

В качестве более конкретного примера я думаю, когда Apple представила firstObject на NSArray в iOS 7, но задним числом сделала его доступным для iOS 4 (где она была доступна, но была закрыта). Любой код с использованием respondsToSelector: работал бы на iOS < 7, но, очевидно, проверка версии не удалась.

Есть ли какие-либо преимущества для перехода на ключевое слово #available, которое я пропустил?

+0

@holex Они оба являются решениями времени исполнения. – Sulthan

+0

Я думаю, причина в том, что Swift не позволяет нам создавать ссылки на функции или методы (кроме текстовых), и это тоже опасно. Использование любого вида 'responsesToSelector' в Swift выглядит уродливо, потому что вы должны указать имя метода как строку (также в условном названии метода Obj-C) :) Я не уверен, что' # available' является улучшением, но это будет способствовать согласованности кода, потому что рано или поздно появятся скоростные рамки Swift. С другой стороны, проверка версии доступна только для системной версии, поэтому для внешних библиотек вам все равно придется использовать проверку выбора. – Sulthan

+0

Библиотеки @Sulthan External (Swift 2) могут аннотировать свой собственный API с ключевым словом '@ availability'. В качестве побочной заметки я не уверен в совместимости с использованием 'if #available ...' с помощью методов Objective-C, аннотированных с помощью' NS_AVAILABLE'. Я полностью согласен с тем, что 'responsesToSelector:' имеет свои проблемы даже в Objective-C, и необходимость указывать селектор как строку в Swift просто ужасна. Я все по-хорошему, но #доступный (кажется мне) как побочный шаг, а не шаг вперед. –

ответ

3

Большое преимущество заключается в том, что компилятор Swift 2 в Xcode 7 сравнивает доступность классов, методов, свойств, ... от развертывания цели вашего проекта.

Использование respondsToSelector всегда было подвержено ошибкам. Для Objective-C,

if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)]) { 

компилятор проверяет, только если requestWhenInUseAuthorization некоторые известный метод, но он не может проверить, если проверка логически правильно.

В Swift это еще хуже, потому что селекторы могут быть указаны только как строки , а компилятор ничего не проверяет.

С Свифтом 2/Xcode 7, соответствующий код

let locationManager = CLLocationManager() 
if locationManager.respondsToSelector("requestWhenInUseAuthorization") { 
    locationManager.requestWhenInUseAuthorization() 
} 

не компилируется больше, если цель развертывания меньше, чем прошивка 8, сообщение об ошибке

 
error: 'requestWhenInUseAuthorization()' is only available on iOS 8.0 or newer 
      locationManager.requestWhenInUseAuthorization() 
          ^
note: add 'if #available' version check 

С

let locationManager = CLLocationManager() 
if #available(iOS 8.0, OSX 10.10, *) { 
    locationManager.requestWhenInUseAuthorization() 
} 

компилятор знает, что метод будет вызван iOS 8 и более поздних версий и не жалуется.

+1

Yeh Я видел, что в документах, наверное, у моего вопроса нет ответа. Мне просто кажется странным, что они не предоставили что-то вроде необязательного синтаксиса метода протокола для проверки API: 'if locationManager.requestWhenInUseAuthorization? {locationManager.requestWhenInUseAuthorization()} else {// метод недоступен} ' –

+0

@SteveWilford:' if # available' работает не только с классами и методами, но и со свойствами. Если я правильно помню, что-то вроде 'if someInstance.someOptionalProperty? {...} 'невозможно в Swift. Была дискуссия о SO или на форуме разработчиков Apple, но я не нашел ее снова. –

+0

Хороший вопрос, я не рассматривал дополнительные свойства. 'if # someInstance.someOptionalProperty {...}' начинает становиться глупым, я думаю, именно поэтому я не инженер-компилятор. Может быть, после нескольких встреч с '# available' я привык к этому ... –

0

respondsToSelector проверяет время выполнения, чтобы увидеть, присутствует ли селектор. #available проверяет опубликованный SDK, чтобы узнать, включено ли оно.В видео WWDC они прямо заявили, что многие публичные API-интерфейсы начинаются как частные API в более ранних версиях ОС. Это означает, что в некоторых случаях это не будет #available, хотя это respondsToSelector

Смежные вопросы