将@available 与存储属性一起使用

Using @available with stored properties

我有一个使用本地通知并支持 iOS10 的应用程序。我正在尝试添加 iOS9 支持,这需要我使用旧的位置通知 API。我正在尝试在我的 iOS 10 代码上使用 @available#available,但我不知道如何让我的中心变量仅适用于设备 运行 iOS10.

当我将目标从 iOS 10 设置为 9 时,我收到此变量的错误消息:

UNUserNotificationCenter is only available on iOS 10.0 or newer.

它建议我将 @available(iOS 10.0, *) 添加到我的整个 class 中,但我不想这样做,因为此 class 中的代码将用于 iOS 9. 我感谢任何关于如何将我的中心 属性 限制为 iOS 10.

的建议
class ViewController: UIViewController, UITextFieldDelegate {
  
  let center = UNUserNotificationCenter.current()
  ...
}

@available 可用于整个 class 或一个或多个函数,但不能用于属性。

关于您的 UNUserNotificationCenter 用法,current returns 一个永远不会改变的单例,所以为什么不直接删除 center 常量,而只使用 UNUserNotificationCenter.current() 其中 center被使用了?

let validClass = NSClassFromString("UNUserNotificationCenter") != nil

使用 validClass 来确定特定于 iOS 10 的代码,例如:

if validClass
   // iOS 10 code
else
   // Earlier than iOS 10 code

这是一个可能的解决方案(感谢 blog post)。这个想法是使用类型为 Any 的存储 属性,然后创建一个计算的 属性,它将转换存储的 属性(并在必要时实例化它)。

private var _selectionFeedbackGenerator: Any? = nil
@available(iOS 10.0, *)
fileprivate var selectionFeedbackGenerator: UISelectionFeedbackGenerator {
    if _selectionFeedbackGenerator == nil {
        _selectionFeedbackGenerator = UISelectionFeedbackGenerator()
    }
    return _selectionFeedbackGenerator as! UISelectionFeedbackGenerator
}

另一个是使用lazy(但是,这使得变量可读写):

@available(iOS 10.0, *)
private(set) lazy var center = UNUserNotificationCenter.current()

与 kgaidis 类似的想法,通过使用任何版本都接受的类型的单独存储 属性。但是 Any 可能太通用了,因为它不能被声明为 weak 例如,所以在某些情况下你可能想用一个符合协议的协议来替换它:

private weak var _notificationCenterDelegate: NSObjectProtocol?
@available(iOS 10.0, *)
var notificationCenterDelegate: UNUserNotificationCenterDelegate? {
    return _notificationCenterDelegate as? UNUserNotificationCenterDelegate
}

我知道这是一个较旧的问题,但我想为像我一样通过 Google 来到这里的人添加一个答案。

正如 kgaidis 和 Coeur 提到的,您可以在计算属性上使用 @available。但是,lazy 变量被视为计算属性,因此您也可以对它们使用 @available。这有一个很好的好处,那就是删除额外存储的 属性 和强制转换的样板 - 事实上,它不会在你的 pre-iOS 10 代码中留下 属性 的证据。

您可以像这样简单地声明它:

@available(iOS 10.0, *)
private(set) lazy var center = UNUserNotificationCenter.current()

不幸的是,没有办法让它完全只读,但 private(set) 至少让它在 class.

之外只读

我在我的应用程序中用于反馈生成器的代码支持 iOS 9. 如您所见,它很简单并且没有强制转换。主要思想是将值存储在 Any? 属性 中并通过计算值使用它。

private var storedFeedbackGenerator: Any? = nil
@available(iOS 10.0, *)
private var feedbackGenerator: UISelectionFeedbackGenerator {
    if let generator = storedFeedbackGenerator as? UISelectionFeedbackGenerator {
        return generator
    }

    let generator = UISelectionFeedbackGenerator()
    generator.prepare()
    storedFeedbackGenerator = generator
    return generator
}

对于objective-c

@property (nonatomic, strong) CustomClass *object API_AVAILABLE(ios(10.0));