Ios Lazy/inline在Swift中实现协议

Ios Lazy/inline在Swift中实现协议,ios,swift,protocols,inline,lazy-evaluation,Ios,Swift,Protocols,Inline,Lazy Evaluation,我想用Swift实现一个协议。 所以在实现的时候,我可以访问协议范围之外的变量 与在Java中实现接口而不声明类相同: class MyClass:UIView { var someComponent:SomeInnerComponent = SomeInnerComponent(); var count:Int = 0; var a = :SomeProtocol { //<----- IS THIS POSSIBLE, IF YES HOW ? func a0

我想用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()
}
类MyClass:UIView{
var someComponent:SomeInnerComponent=SomeInnerComponent();
变量计数:Int=0;

var a=:SomeProtocol{/您正在寻找的是一个内部类(不一定是匿名类),在一个作用域中声明,该作用域允许它访问
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
实例。而且没有方法引用该
MyView
实例(或其属性)从
Helper
的方法中,因为您可以构造一个
MyView.Helper()
,而不需要现有的
MyView
实例。Swift-nest中的内部类(或通常的嵌套类型)只在词法范围内,而不在存在所有权内。(或者换一种说法,因为您引用了Java:Swift中的所有内部类都类似于Java中的静态内部类。没有非静态内部类。)不过,如果这是您想要的特性

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

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

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(dec:(Void->Void),inc:(Void->Void))
    编写(或以其他方式指定)这些闭包
  • 因为您可以在初始化
    帮助程序时编写闭包,所以这些闭包可以捕获调用初始值设定项的变量,包括隐式
    self
    ,它引用创建
    帮助程序的
    MyClass
    实例
您需要
a0
/
a1
dec
/
inc
,因为您需要闭包(后者),而不是方法来捕获封闭状态。即使闭包和func/方法在许多方面是可互换的,您也无法通过将闭包指定给方法/func名称来创建方法/func实现。(如果
SomeProtocol
需要闭包属性而不是方法,情况就不同了,但我假设
SomeProtocol
不在您的控制之下。)

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



注意:我的示例使用了闭包
{self.count-=1}
,您可能希望在这里使用
{self.count-}
。后者不起作用,因为这是一个带值的表达式,所以Swift会将其解释为闭包返回值的缩写。然后它会抱怨您分配了
()->Int
关闭一个属性,该属性需要一个
()->()
(又称
Void->Void
)关闭。使用
-=1
可以解决这个问题。

我会选择另一种方法,我知道这是一个非常古老的主题,但只是为了防止其他人遇到这个问题:

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都可以这样使用


请注意,关于讨论闭包的其他问题的所有答案都解决了您关于“访问协议范围外的变量”的问题闭包可以捕获和修改这些变量。谢谢,我查看了这个解决方案,但没有看到如何访问父类的变量。所有示例都显示了一个匿名类,但没有一个示例正在访问父变量。谢谢!很好的hack:),这个例子可以帮助我,为什么我需要'dec','inc'方法。我可以直接使用a0,01吗?这是swift中的限制吗?