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

Why can't closures close over instance members

我非常喜欢 Sulthan 的回答(在 Anonymous class in swift 中) 它描述了构建一个符合协议但其 class 隐藏在闭包中的对象。这对于构建单例并且不会污染名称-space 与 class 类似 Recorder1、Recorder2...

非常有用

但是,当我尝试用它做任何有用的事情时,我失败了,因为闭包不会关闭内部 class.

内部的外部 class' 实例成员
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 会引发编译器错误:实例成员 'events' 无法使用 .

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

Marius 的回答(在 中)建议您不能在定义属性时访问实例成员,所以我稍后也尝试像这样计算侦听器。

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

但是编译器说它不能访问外部自身

如果闭包无法访问外在自我,则它们似乎非常无能为力。在 Java 中,你可以通过 Recorder.self.events 之类的东西到达外在的自我。并且 Recorder.self. 可能仅在存在名称冲突时才需要(?)

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

你会怎么写它 RecorderDistributor 一个除了接收 handleEvent 消息什么也做不了的对象?

非常感谢。

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

此实现没有 EventListener 协议。我确信有一种简单的方法可以做到这一点,但对于嵌套 类 来说,这似乎有点过分了。所以 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)

使用 Price Ringo 的 2 阶段初始化,并让 Recorder 将 self 传递到内部 class,我得到了我正在寻找的 class 隐藏。它比我希望的要冗长一点,但具有使 Recorder 实例成员的生命周期清晰的优点。例如,它避免了围绕 events 的复制与引用混淆。 self 还可以轻松地以另一种方式传递数据:adjustment

Price Ringo 的解决方案更优雅,更适合这个特定问题。此解决方案更通用,适用于任何现有协议。总的来说,我正在尝试将面向协议的模式合并到我的代码中。此解决方案创建的对象除了它们遵循的协议外没有其他类型。

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

class Recorder { 
    var events = [Int]() // example of data out
    let adjustment: Int  // example of data in
    var listener: EventListener = { 
        class __: EventListener { 
            func handleEvent(event: Int) { } 
        } 
        return __()
    }()
    init(adjustment: Int){ 
        self.adjustment = adjustment 
        initializeListener()
    }
    private func initializeListener() {
        listener = {
            class __: EventListener {
                unowned var recorder: Recorder
                init(recorder: Recorder){
                    self.recorder = recorder
                }
                func handleEvent(event: Int) {
                    let adjustedEvent = 
                            recorder.adjustment * event
                    recorder.events.append(adjustedEvent)
                 // print("Recorded: \(adjustedEvent)")
                }
            }
            return __(recorder: self)
        }()
    }
}// Recorder

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

var d = Distributor()
var r1 = Recorder(adjustment: 2)
var r2 = Recorder(adjustment: 3)

d.addListener(r1.listener)
d.addListener(r2.listener)
d.distributeEvent(10)

print(r1.events) // [20]
print(r2.events) // [30]

如果你还在阅读,我有一个问题。我已将实例成员 __>recorder 设置为无主以避免保留。我还需要让 Recorder>listener 无主吗?