【问题标题】:Use different implementations of a class depending on iOS version?根据 iOS 版本使用不同的类实现?
【发布时间】:2017-09-07 16:40:16
【问题描述】:

iOS 11 最近添加了一个我想使用的新功能,但我仍然需要支持旧版本的 iOS。有没有办法将同一个类编写两次,让新版本的 iOS 使用一个版本的类,而旧版本的 iOS 使用另一个?

(注意:最初我使用if #available(iOS 11, *),但我不得不在很多地方使用它,我认为如果可能的话,拥有两个版本的类会更干净。也许有一种使用@的方法以某种方式可用?我专注于使用@available 而不是预编译器#IFDEF 的东西,因为现在似乎“可用”标签是在 Swift 中执行此操作的首选方式?)

【问题讨论】:

  • 如果您在问题中包含一些相关代码会有所帮助。举例说明你正在做什么,并清楚地描述你想做什么。
  • 我认为真正的问题是#available 没有对话。例如,如果这是 iOS 11,您可以包含内容,但如果这是 iOS 11,您不能排除内容。
  • 您的两个版本的班级成员是否完全相同?
  • 它没有相同的成员。其中一个版本的类变量大约是另一个版本的一半。基本上我有一个使用 Reachability 重新进行网络调用的服务器类,iOS 11 引入了一个“waitsForConnectivity”标志,听起来它可能会做很多我所做的事情,所以我不需要那么多变量来跟踪网络的东西。我将尝试找出我可以稍后发布的代码的精简版本。
  • @matt 当然可以。只需在 if 块之后包含一个 else 块。

标签: ios swift availability


【解决方案1】:
protocol Wrapper {

}

extension Wrapper {
    static func instantiate(parametersAB: Any*) -> Wrapper{
        if #available(iOS 11.0, *) {
            return A(parametersA)
        } else {
            return B(parametersB)
        }
    }
}

@available(iOS 11.0,*)
class A: Wrapper {
    //Use new feature of iOS 11
    init(parametersA: Any*) {
      ...
    }
}

class B: Wrapper {
    //Fallback
    init(parametersB: Any*) {
      ...
    }
}

现在您通过调用 Wrapper.instationate(params) 获取您的实例,这样您就可以忘记检查其余代码,并尽可能使用新功能。

只有当你可以为两个类建立相同的接口时,这个解决方案才可能实现(即使它是方法的虚拟版本)。

【讨论】:

    【解决方案2】:

    高质量的代码遵循几个原则。一个是单一责任原则,它指出一个班级应该只有一个责任,或者 - 正如鲍勃叔叔所说 - 改变班级应该只有一个原因。
    另一个原则是依赖倒置原则:一个类不应依赖于较低级别的类,而应依赖于这些较低级别的类实现的抽象(协议)。这也意味着所有依赖项都必须传递到使用它们的类中。

    应用于您的问题,一种解决方案可能是:

    1. 视图控制器具有定义为协议的数据源属性。
    2. 有几个类实现了这个协议,每个类都对应不同的 iOS 版本。
    3. 存在一个课程,它只准备选择正确的版本。这个版本选择可以通过多种方式完成,我坚持#available

    数据源协议:

    protocol ViewControllerDataSourcing: class {
        var text:String { get }
    }
    

    及其实现:

    class ViewControllerDataSourceIOS10: ViewControllerDataSourcing {
        var text: String {
            return "This is iOS 10"
        }
    }
    
    class ViewControllerDataSourceIOS11: ViewControllerDataSourcing {
        var text: String {
            return "This is iOS 11"
        }
    }
    
    class ViewControllerDataSourceIOSUnknown: ViewControllerDataSourcing {
        var text: String {
            return "This is iOS Unknown"
        }
    }
    

    选择正确版本类的类:

    class DataSourceSelector{
        class func dataSource() -> ViewControllerDataSourcing {
            if #available(iOS 11, *) {
                return ViewControllerDataSourceIOS11()
            }
            if #available(iOS 10, *) {
                return ViewControllerDataSourceIOS10()
            }
            return ViewControllerDataSourceIOSUnknown()
        }
    }
    

    最后是视图控制器

    class ViewController: UIViewController {
    
        var dataSource: ViewControllerDataSourcing?
        
        @IBOutlet weak var label: UILabel!
        override func viewDidLoad() {
            super.viewDidLoad()
            self.dataSource = DataSourceSelector.dataSource()
            label.text = dataSource?.text
        }
    }
    

    这是一个非常简单的示例,应该突出显示负责的不同组件。

    【讨论】:

      【解决方案3】:

      “一个类的两个版本”听起来像是一个基类和两个子类。您可以在运行时根据系统版本实例化所需的子类,例如查询进程信息。

      【讨论】:

        【解决方案4】:

        我遇到了同样的问题。我最终通过在我只想在特定版本的 iOS 中支持的方法上方添加 @available 来解决这个问题:

        @available(iOS 11.3, *)
        func myMethod() { .. }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-07-26
          • 1970-01-01
          • 1970-01-01
          • 2010-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多