如何将闭包作为变量传递?

How to pass closure as a variable?

我有以下来自 playground 的代码:

class MyButton {
    var pressed: (() -> Void)?
    
    func press() {
        pressed?()
    }
}

class SomeView {
    var buttonDidPressed: (() -> Void)?
    
    let button = MyButton()
    
    init() {
        button.pressed = buttonDidPressed
    }
    
    func press() {
        button.press()
    }
}

let view = SomeView()

view.buttonDidPressed = {
    print("pressed")
}

view.press()

MyButton 有闭包 pressed,它在调用 press 时执行。在 SomeViews init 中,我为 pressed 闭包赋值。当然,在初始化时,它是nil。但是在 SomeView 初始化之后,我给那个 buttonDidPressed 闭包赋值。因为,闭包是引用类型,当 view.press 被调用时, buttonDidPressed 不应该是 nil。但是,无论如何,我的打印没有执行。为什么?

您首先初始化 SomeView(调用 init 函数),然后设置 buttonDidPressed 属性。所以,buttonDidPressed 属性 有你的闭包,但从未将它传递给 MyButton 实例。

您可以使用didSet立即更新:

class SomeView {
    var buttonDidPressed: (() -> Void)? {
        didSet {
            button.pressed = buttonDidPressed
        }
    }
    
    let button = MyButton()
    
    func press() {
        button.press()
    }
}

更新:你也可以像这样在SomeView的初始化器中传递你的闭包:

class SomeView {
    let button = MyButton()
    
    init(buttonDidPressed: (() -> Void)?) {
        button.pressed = buttonDidPressed
    }
    
    func press() {
        button.press()
    }
}

let view = SomeView {
    print("pressed")
}

view.press()

其实这个问题最好用绑定来解释,比方说:

class Sekoya<T> {
typealias Listener = (T) -> Void
var listenerHolder: Listener?
var value: T {
    didSet {
        listenerHolder?(value) // when value change listenerHolder triger
    }
}

init(_ value: T) {
    self.value = value
}

func bind(listener: Listener?) {
    
    self.listenerHolder = listener // referencing listener my closure variable in SomeView Class
    listener?(value)
    
}
}

让我们在另一个地方使用这个绑定 class:

 let sekoyaInt = Sekoya(25)
    sekoyaInt.bind {
            print("value changed: \([=11=])")
        }

每当您更改值时,您的引用闭包将被触发并且值将被更改并打印值

以下是我认为您想要实现的目标:

class SomeView {
    var buttonDidPressed: (() -> Void)? = nil {
       didSet {
          button.pressed = buttonDidPressed
       }
    }
    
    let button = MyButton()
    
    func press() {
        button.press()
    }
}

您的代码无法正常工作的原因是因为在 SomeView.init 中,buttonDidPressednil - 即缺少参考。所以:

buttonDidPressed = nil
button.pressed = buttonDidPressed 
// is equivalent to
button.pressed = nil

稍后为 buttonDidPressed 分配新的(或不同的)引用不会更改 button.pressed 属性。