Swift #available 关键字与 respondsToSelector
Posted
技术标签:
【中文标题】Swift #available 关键字与 respondsToSelector【英文标题】:Swift #available keyword vs respondsToSelector 【发布时间】:2015-09-11 08:04:44 【问题描述】:根据pre-release Swift 2 documentation,现在有一个#available
关键字可以与if let
或guard
语句一起用于检查API 可用性。它给出了以下示例:
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 在 iOS 7 中在 NSArray
上引入 firstObject
但追溯使其可用于 iOS 4(它可用,但私有)。任何使用 respondsToSelector:
的代码都可以在 iOS
改用我错过的#available
关键字有什么好处吗?
【问题讨论】:
@holex 它们都是运行时解决方案。 我认为原因是 Swift 不允许我们创建对函数或方法的引用(文本除外),这也很危险。在 Swift 中使用任何类型的respondsToSelector
看起来很难看,因为您必须将方法的名称指定为字符串(也在 Obj-C 方法名称约定中):) 我不确定 #available
是一种改进,但它会帮助代码保持一致性,因为迟早会有 Swift 框架。另一方面,版本检查仅适用于系统版本,因此对于外部库,您仍然必须使用选择器检查。
@Sulthan External (Swift 2) 库可以使用 @availability
关键字注释自己的 API。作为旁注,我不确定使用if #available...
与使用NS_AVAILABLE
注释的Objective-C 方法之间的兼容性。我完全同意 respondsToSelector:
即使在 Objective-C 中也存在问题,并且必须在 Swift 中将选择器指定为字符串真是太糟糕了。我完全赞成一种更好的方式,但#available 似乎(对我来说)是一个侧步,而不是向前迈出的一步。
我也有同样的感觉。
AFAIK firstObject
方法的情况在 Swift 中是不可能的。而且因为这是不可能的,我会假设 if #available
方法适用于 Swift 中的所有情况。
【参考方案1】:
一个很大的优势是 Xcode 7 中的 Swift 2 编译器将 对部署的类、方法、属性等的可用性 项目的目标。
使用respondsToSelector
总是容易出错。对于 Objective-C,
if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)])
编译器仅验证requestWhenInUseAuthorization
是否为一些
已知方法,但无法验证该检查在逻辑上是否正确。
在 Swift 中情况更糟,因为选择器只能指定为 字符串,编译器不会验证任何内容。
使用 Swift 2/Xcode 7,对应代码
let locationManager = CLLocationManager()
if locationManager.respondsToSelector("requestWhenInUseAuthorization")
locationManager.requestWhenInUseAuthorization()
如果部署目标小于 iOS 8,则不再编译, 错误信息是
错误:“requestWhenInUseAuthorization()”仅适用于 iOS 8.0 或更高版本 locationManager.requestWhenInUseAuthorization() ^ 注意:添加 'if #available' 版本检查与
let locationManager = CLLocationManager()
if #available(iOS 8.0, OSX 10.10, *)
locationManager.requestWhenInUseAuthorization()
编译器知道该方法只会在 iOS 8 及更高版本上调用 并且不抱怨。
【讨论】:
是的,我在文档中看到了,我想我的问题并没有真正的答案。我觉得很奇怪,他们没有提供类似于可选协议方法语法来检查 API:if locationManager.requestWhenInUseAuthorization? locationManager.requestWhenInUseAuthorization() else // method is not available
@SteveWilford:if #available
不仅适用于类和方法,还适用于属性。如果我没记错的话,像 if someInstance.someOptionalProperty? ...
这样的东西在 Swift 中是不可能的。在SO或者苹果开发者论坛上有讨论,但是我没有再找到。
好点,我没有考虑可选属性。 if #someInstance.someOptionalProperty ...
开始变得愚蠢,我想这就是我不是编译器工程师的原因。也许在与#available
接触几次后,我会习惯的……【参考方案2】:
respondsToSelector
检查运行时以查看选择器是否存在。 #available
检查已发布的 SDK 是否包含在内。在 WWDC 视频中,他们明确表示,许多公共 API 在早期操作系统版本中都是作为私有 API 开始的。这意味着在某些情况下它不会是#available
,即使它是respondsToSelector
【讨论】:
以上是关于Swift #available 关键字与 respondsToSelector的主要内容,如果未能解决你的问题,请参考以下文章
Parse 和 Swift:didReceiveRemoteNotification 未收到带有“aps”的推送通知:“content-available”:1