【问题标题】:Lazy/inline implement a protocol in Swift惰性/内联在 Swift 中实现协议
【发布时间】:2014-09-06 20:12:19
【问题描述】:

我想在 Swift 中延迟/内联实现一个协议。 所以在实现的时候,我可以访问协议范围之外的变量,

与在 Java 中实现接口而不声明类相同:

class MyClass:UIView {
  var someComponent:SomeInnerComponent = SomeInnerComponent();
  var count:Int = 0;

  var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ?
      func a0() {MyClass.count--}
      func a1() {MyClass.count++}
  } 

  someComponenet.delegate = a;
}

protocol SomeProtocol {
  func a0()
  func a1()
}

编辑----

感谢我查看了这个解决方案,但我没有看到如何访问父类的变量。 所有示例都显示了一个匿名类,但没有一个示例访问父变量。

【问题讨论】:

  • 请注意,关于讨论闭包的其他问题的所有答案也解决了您关于“访问协议范围之外的变量”的问题。闭包可以捕获和修改这些变量。
  • 感谢我查看了这个解决方案,但我没有看到如何访问父类的变量。所有示例都显示了一个匿名类,但没有一个示例访问父变量。

标签: ios swift protocols inline lazy-evaluation


【解决方案1】:

您正在寻找的是一个内部类(不一定是匿名的),在一个范围内声明,允许它访问 MyClass 实例的 count 变量,并采用在不同的地方定义的协议范围。现在 Swift 有一些这样的部分,但看起来你不能以任何你想要的那样简洁的方式把它们放在一起。

你可能会考虑声明一个内部类:

class MyView: UIView {
    let someComponent = SomeInnerComponent() // type SomeInnerComponent is inferred
    var count = 0 // type Int is inferred
    class Helper: SomeProtocol {
        func a0() { count-- } // ERROR
        // ...
    }
    init() {
        someComponent.delegate = Helper()
    }
}

但这行不通,因为count 隐含地是self.count,其中self 是Helper 实例,而不是“拥有”Helper 实例的MyView 实例。而且没有办法从Helper 的方法中引用MyView 实例(或其属性),因为您也可以在没有现有MyView 实例的情况下构造MyView.Helper()。 Swift 中的内部类(或一般的嵌套类型)仅嵌套在词法范围内,而不嵌套在存在所有权中。 (或者换一种说法,因为您引用了 Java:Swift 中的所有内部类都类似于 Java 中的静态内部类。没有非静态内部类。)不过,如果这是您想要的功能,it's probably worth telling Apple you want it。

您也可以尝试在 MyView.init() 中声明 Helper -- 在 Swift 中,您可以在任何地方嵌套类型定义,包括内部函数或其他类型的方法。在那里定义,它可以引用MyView的属性。但是,现在Helper 的类型信息只在MyView.init() 内部可见,所以当你将它分配给someComponent.delegate(其类型只是SomeProtocol)时,你不能使用它......这个甚至使编译器崩溃。 (这是另一个bug to report,但很难说这个错误是真的“编译器在有效使用时崩溃”还是“代码不好,但编译器崩溃而不是产生错误”。)

我能想到的最接近的解决方案如下所示:

class SomeInnerComponent {
    var delegate: SomeProtocol?
}
protocol SomeProtocol {
    func a0()
    func a1()
}
class MyClass  {
    var someComponent = SomeInnerComponent()
    var count = 0
    struct Helper: SomeProtocol {
        var dec: () -> ()
        var inc: () -> ()
        func a0() { dec() }
        func a1() { inc() }
    }
    init() {
        someComponent.delegate = Helper(
            dec: { self.count -= 1 }, // see note below
            inc: { self.count += 1 }
        )
    }
}

它是如何工作的:

  • Helper 是一个内部结构(可以是一个类,但结构更简单)
  • 实现了a0和a1方法,满足SomeProtocol的要求
  • a0 和 a1 的实现调用闭包 dec 和 inc,它们是 Helper 结构的存储属性(也称为实例变量)
  • 在构造 Helper 实例时编写(或以其他方式指定)这些闭包(使用默认的成员初始化器 Helper(dec: (Void -&gt; Void), inc: (Void -&gt; Void)))
  • 因为您可以在初始化Helper 时编写闭包,所以这些闭包可以在您调用初始化程序的位置捕获变量,包括引用MyClass 实例的隐式self 创建Helper。

您需要a0/a1 和dec/inc,因为您需要闭包(后者)而不是方法来捕获封闭状态。即使闭包和函数/方法在许多方面是可互换的,你也不能通过将闭包分配给方法/函数名称来创建方法/函数实现。 (如果SomeProtocol 需要闭包属性而不是方法,那就另当别论了,但我假设SomeProtocol 不在你的控制之下。)

无论如何,这是一种您可能并不真正需要的大量样板和抽象层,因此可能值得研究其他方式来构建您的代码。


注意:我的示例使用闭包 { self.count -= 1 },您可能会期望 { self.count-- }。后者不起作用,因为这是一个有值的表达式,所以 Swift 会将它解释为闭包返回值的简写。然后它会抱怨你将一个() -&gt; Int 闭包分配给一个需要() -&gt; ()(又名Void -&gt; Void)闭包的属性。改用 -= 1 可以解决此问题。

【讨论】:

  • 谢谢!,不错的 hack :),这个例子可以为我服务,为什么我需要 'dec','inc' 方法。我可以直接使用 a0, 01 吗?这是 swift 的限制吗?
【解决方案2】:

我会采用不同的方法,我知道这是一个相当古老的话题,但以防万一其他人遇到这个问题:

class MyClass:UIView {
  var someComponent:SomeInnerComponent = SomeInnerComponent();
  var count:Int = 0;

  init(){
    // Assign the delegate or do it somewhere else to your preference:
    someComponenet.delegate = ProtocolImplementation(myClass: self);
  }

  private class ProtocolImplementation: SomeProtocol {
      let selfReference: MyClass          

      init(myClass: MyClass){
         selfReference = myClass
      } 

      public func a0(){
         selfReference.count--
      }

      public func a1(){
          selfReference.count++
      }
  }
}

protocol SomeProtocol {
  func a0()
  func a1()
}

通过遵循这种方法,还可以多次包含相同的协议,假设您的协议支持泛型并且您希望实现两次。 SomeProtocol 和 SomeProtocol 可以根据需要同时使用。

亲切的问候

【讨论】:

    猜你喜欢
    • 2015-11-02
    • 2015-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 2020-07-24
    相关资源
    最近更新 更多