swift - 为什么闭包不能关闭实例成员

标签 swift closures anonymous-class

我真的很喜欢 Sulthan 的回答(在 Anonymous class in swift 中) 它描述了构建一个符合协议(protocol)但其类隐藏在闭包内的对象。这对于构建单例非常有用,并且不会被 Recorder1、Recorder2 等类污染 namespace ......

但是,当我尝试用它做任何有用的事情时,我失败了,因为闭包不会关闭内部类中外部类的实例成员。

protocol EventListener {
    func handleEvent(event: Int) -> ()
}

class Recorder { 
    static var events = [Int]() // static is forced
    var listener: EventListener = {
        class R : EventListener {
            func handleEvent(event: Int) {
                events.append(event)
                print("Recorded: \(event)")
            }
        }//
        return R()
    }()
}// Recorder

class Distributor {
    var listeners = [EventListener]()
    func register(listener: EventListener){
        listeners.append(listener)
    }
    func distribute(event: Int){
        for listener in listeners {
            listener.handleEvent(event)
        }
    }
}

var d = Distributor()
var r1 = Recorder()
var r2 = Recorder()

d.register(r1.listener)
d.register(r2.listener)
d.distribute(10)

print(Recorder.events) // [10, 10] Same event recorded twice.

上面的代码编译并运行。但我希望 Recorder 中的 events 成为实例成员,以便每个 Recorder 都有自己的记录。删除 static 会引发编译器错误:无法使用实例成员“事件”

我尝试在 Recorder 中定义一个实例 func record(event)handleEvent(event) 调用,但出现了相同的错误。

Marius 的回答(在 Instance member cannot be used on type | Closures 中)建议您在定义属性时无法访问实例成员,因此我稍后也尝试像这样计算监听器。

class Recorder { 
    var events = [Int]()
    var listener: EventListener {
        class R : EventListener {
            func handleEvent(event: Int) {
                events.append(event) // error: can't access events
                print("Recorded: \(event)")
            }
        }
        return R()
    }
}// Recorder

但是编译器说它无法访问外部自身

如果闭包无法访问外部 self ,那么它们看起来就非常无能为力。在 Java 中,您可以使用诸如 Recorder.self.events 之类的东西来获取外部 self。并且 Recorder.self. 可能仅在存在名称冲突时才需要(?)

Swift 是这样设计的还是我错过了什么?

你会如何编写它,以便RecorderDistributor提供一个除了接收handleEvent消息之外什么都不做的对象?

非常感谢。

最佳答案

我不相信有办法在部分初始化的记录器中关闭事件。但是,您可以为记录器创建一个两阶段的初始化过程。第一阶段使用默认的空监听器创建 Recorder 对象。第二阶段创建一个适当的事件闭包并将其分配给监听器。

此实现没有 EventListener 协议(protocol)。我确信有一种简单的方法可以做到这一点,但对于嵌套类来说,这似乎有点矫枉过正。因此,Distributor 不是包含对象数组,而是包含闭包。我们让 Distributor 直接包含闭包数组。

除了对 initialize 的额外调用之外,您的 API 不会更改。

typealias EventListener = (Int) -> ()

class Distributor {
  var listeners = [EventListener]()
  func register(listener: EventListener){
    listeners.append(listener)
  }
  func distribute(event: Int){
    for listener in listeners {
      listener(event)
    }
  }
}

class Recorder {
  var events = [Int]()
  var listener: EventListener = {
    (event: Int) in
    fatalError("Listener not initialized")
  }
  func initialize() {
    listener = {
      (event: Int) in
      self.events.append(event)
      print("Recorded: \(event)")
    }
  }
}

var d  = Distributor()

// 1st stage of Recorder instantiation
var r1 = Recorder()
var r2 = Recorder()

// 2nd stage
// r1.listener can now have a closure that includes an init'ed events
r1.initialize()
r2.initialize()

d.register(r1.listener)
d.register(r2.listener)
d.distribute(10)

print(r1.events)
print(r2.events)

关于swift - 为什么闭包不能关闭实例成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35970401/

相关文章:

ios - 如何重构 swift 代码以将协议(protocol)和结构类型作为方法参数

ios - 在准备 segue 之前从闭包中提取数组 - Swift

匿名对象方法的Javadoc

java - 有人可以向我解释一下这个 Java 语法吗?

ios - 如何通过 UnsafePointer<Int32> 创建 Int32?

Swift 3 : get information from server in ViewDidLoad and when click a button, 等待服务器的信息响应

ios - 调用文本字段 :shouldReplaceCharactersInRange from a delegate method

JavaScript 闭包 : Pratical Closures example in MDN

Javascript do while、setTimeout、事件变量范围

java - 为什么我不能在匿名类中使用 <Class>.this?