如何将闭包作为参数传递给perform(selector,withObject)

时间:2019-03-25 14:37:32

标签: swift

我正在尝试将闭包作为参数快速传递并在选择器方法中执行。

override func viewDidLoad() {
    super.viewDidLoad()

    let closure = {
        print(self.isCityChoosen)
    }
    perform(#selector(foo(param:)), with: closure)
}

@objc func foo(param: () -> () ) {
    param()
}

但是我有

Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) 执行param()时运行时错误 为什么会发生此错误? 有办法解决吗?

3 个答案:

答案 0 :(得分:6)

您的闭包不是Objective-C块,因此无法通过ObjC运行时传递。您必须使用@convention将其标记为块。

let closure: @convention(block) () -> Void = { ... }

您可以通过分配现有的闭包将其转换为块:

let closure = { ... }
let block: @convention(block) () -> Void = closure
perform(#selector(foo(param:)), with: block)

Objective-C块实际上是对象,并参与ARC。之所以发生崩溃,是因为perform试图在非阻塞状态下调用Block_copy

当然,选择器并不是Swift中合适的工具,因此您应该将任何基于选择器的接口转换为仅使用函数参数。如果您发现自己使用perform,则可能是您在Swift中走错了路。但是如果需要,它仍然可用。

答案 1 :(得分:1)

是否有使用perform(_:with:)的理由?如果不是这样,您可以简化代码,只需使用闭包

调用您的方法
override func viewDidLoad() {
    super.viewDidLoad()

    foo {
        print(self.isCityChoosen)
    }
}

func foo(param: () -> () ) {
    param()
}

答案 2 :(得分:1)

我认为这是因为您在方法声明中使用了非object-c类型:

@objc func foo(param: () -> ())闭包不是Objective-C类型。

您可以执行以下操作;

@objc func foo(_ closure: Any) {
    if let closure = closure as? () -> Void {
        closure()
    }
}

之所以起作用,是因为(我猜)您告诉objc运行时该方法接受Any,该id正确地转换为object-c类型IMPORTRANGE