美文网首页
@convention 使用-在 Swift 中通过 perfo

@convention 使用-在 Swift 中通过 perfo

作者: 黄二瓜 | 来源:发表于2020-08-21 10:53 被阅读0次

    最近在使用开发过程中碰到这样一个问题:向一个修饰为 @objc 的方法中传入一个闭包参数,示例代码如下:

    class SomeClass: NSObject {
        @objc func foo() {
            print("foo...")
            let selectorName = "bar:"
            let selector = Selector(selectorName)
            if self.responds(to: selector) {
                let closure = { () in
                    print("block")
                }
                self.perform(selector, with: closure) //this will take error
                //self.bar(block)
            }
        }
    
        @objc func bar(_ arg1:@escaping ()->()) {
            print("bar...")
            arg1()
        }
    }
    
    let someClass = SomeClass()
    someClass.foo()
    

    如果通过 self.perform(selector, with:block) 方法动态调用 bar 方法并传递closure 的话,会报错。

    error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x8).
    

    但是如果直接通过 self.bar(block) 进行调用则不会有问题。

    最终在
    # How to pass closure as a parameter in perform(selector, withObject)
    # How to pass block arguments through performSelector?

    找到了原因:
    Swift 中的 closure 闭包与 Objective-C 中的 block 并不相同,因此不能在运行时阶段作为id类型的参数传递给一个接收id类型参数的方法 。

    This method is the same as perform(_:) except that you can supply an argument for aSelector. aSelector should identify a method that takes a single argument of type id.id is a pointer to an objective-c object. From Swift, you are going to be limited to passing objects that inherit from NSObject. A closure does not meet those requirements.

    解决办法两种:

    1. 使用 @convention 对 Swift 中的闭包进行修饰
    let closure = {
        print("block")
    }
    let block: @convention(block) () -> () = closure
    self.perform(selector, with: block)
    

    关于 @convention 说明可以参考 # 每周 Swift 社区问答:@convention

    @convention特性是在 Swift 2.0 中引入的,用于修饰函数类型,它指出了函数调用的约定。用在以下几个地方:
    修饰 Swift 中的函数类型,调用 C 的函数时候,可以传入修饰过@convention(c)的函数类型,匹配 C 函数参数中的函数指针。
    修饰 Swift 中的函数类型,调用 Objective-C 的方法时候,可以传入修饰过@convention(block)的函数类型,匹配 Objective-C 方法参数中的 block 参数

    2.自定义一个Block类对 Swift 中的闭包进行包装

    class Block: NSObject {
        let block: () -> ()
        init(block: @escaping () -> ()) {
            self.block = block
            super.init()
        }
    }
    
    class SomeClass: NSObject {
        @objc func foo() {
            print("foo...")
            let selectorName = "bar:"
            let selector = Selector(selectorName)
            if self.responds(to: selector) {
                let block = Block {
                    print("block")
                }
                self.perform(selector, with: block)
            }
        }
    
        @objc func bar(_ arg1: Block) {
            print("bar...")
            arg1.block()
        }
    }
    
    let someClass = SomeClass()
    someClass.foo()
    

    相关文章

      网友评论

          本文标题:@convention 使用-在 Swift 中通过 perfo

          本文链接:https://www.haomeiwen.com/subject/lmxujktx.html