【问题标题】:Using @available with stored properties将 @available 与存储的属性一起使用
【发布时间】:2017-06-13 18:28:20
【问题描述】:

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

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

UNUserNotificationCenter is only available on iOS 10.0 or newer.

它建议我将 @available(iOS 10.0, *) 添加到我不想做的整个课程中,因为此类中的代码将用于 iOS 9。我感谢有关如何将我的 center 属性限制为仅的任何建议iOS 10。

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

【问题讨论】:

    标签: ios swift uilocalnotification


    【解决方案1】:

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

    关于你的 UNUserNotificationCenter 使用,current 返回一个永远不会改变的单例,那么为什么不直接删除 center 常量,而只使用 UNUserNotificationCenter.current() 来使用 center 呢?

    【讨论】:

      【解决方案2】:
      let validClass = NSClassFromString("UNUserNotificationCenter") != nil
      

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

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

      【讨论】:

      • 这不是检查可用性的好方法。 Apple 的首选方法是if #available() 语法。但是,这两种方法(此答案或 if #available)都不能用于存储的属性。
      【解决方案3】:

      这是一种潜在的解决方案(感谢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
      }
      

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

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

      【讨论】:

      • 这很聪明! :)
      • 有没有办法在 Objective-C 中实现同样的目标?
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

        正如 kgaidis 和 Cœur 所提到的,您可以在计算属性上使用 @available。但是,lazy 变量被视为计算属性,因此您也可以在它们上使用@available。这具有删除额外存储属性的样板和强制转换的好处 - 事实上,它不会在 iOS 10 之前的代码中留下任何属性的证据。

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

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

        不幸的是,没有办法让它完全只读,但 private(set) 至少让它在类之外只读。

        【讨论】:

        • 感谢分享!
        • 对我来说,这在构建时有效,但在运行时无效:stackoverflow.com/questions/62301128/…
        • @lewis 我无法在 iOS 12.4 设备上重现这一点,但您可能需要像一个答案建议的那样研究弱链接框架
        • 另一种方法是:@available(iOS 14.0, *); private(set) lazy var whateverClass: WhateverClass? = nil
        【解决方案6】:

        我在支持 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
        }
        

        【讨论】:

          【解决方案7】:

          对于objective-c

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-02-24
            • 2016-03-31
            • 1970-01-01
            • 2022-10-17
            • 2014-08-16
            • 1970-01-01
            • 2018-05-23
            • 1970-01-01
            相关资源
            最近更新 更多