Swift #available keyword vs respondsToSelector

亡梦爱人 提交于 2019-12-24 05:47:19

问题


According to the pre-release Swift 2 documentation there is now an #available keyword that can be used with an if let or guard statement for checking API availability. It gives the following example:

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

Or

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

Which it (the doc) states is the equivalent of the following Objective-C code:

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

Obviously respondsToSelector: only works on subclasses of NSObject whereas the #available keyword will work even for "pure" Swift code, so I appreciate that advantage.

However since starting iOS development I've always been led to believe that the best practice for this situation is to detect the presence of an API rather than rely on the version it was introduced.

As a more concrete example I'm thinking of when Apple introduced firstObject on NSArray in iOS 7 but retroactively made it available back to iOS 4 (where it was available, but private). Any code using respondsToSelector: would have worked on iOS < 7 but obviously a version check would fail.

Are there any benefits to shifting to the #available keyword that I've missed?


回答1:


A big advantage is that the Swift 2 compiler in Xcode 7 compares the availability of classes, methods, properties, ... against the deployment target of your project.

Using respondsToSelector was always error-prone. For Objective-C,

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

the compiler only verifies if requestWhenInUseAuthorization is some known method, but it cannot verify if that check is logically correct.

In Swift it is even worse because selectors can only be specified as strings and the compiler does not verify anything.

With Swift 2/Xcode 7, the corresponding code

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

does not compile anymore if the deployment target is less than iOS 8, the error message is

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

With

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

the compiler knows that the method will only be called on iOS 8 and later and does not complain.




回答2:


respondsToSelector checks the the runtime to see if the selector is present. #available checks the published SDK to see if it's included. In the WWDC video, they explicitly said that many public APIs start as private APIs in earlier OS versions. Which means in some cases it won't be #available even though it respondsToSelector



来源:https://stackoverflow.com/questions/32518548/swift-available-keyword-vs-respondstoselector

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!