通过`id`在Objective-C中调用Swift闭包

时间:2017-06-24 02:55:03

标签: objective-c swift closures block

这个问题受到了这个问题的启发:Swift closure in array becomes nil in Objective-c,我使用包装类回答了一个解决方法,但我仍然很好奇为什么一个人无法调用,在Objective中-C,通过id传递的Swift闭包。一个简单的例子如下。

Objective-C代码:

// In a header
typedef void (^EmptyBlock)();

@interface MyClassOC : NSObject

-(void)invoke:(id)blk;

@end

// In an implementation (.m) file
@implementation MyClassOC
-(void)invoke:(id)blk {
    EmptyBlock emptyBlock = blk;
    emptyBlock();
}
@end

提供Objective-C块没问题:

EmptyBlock block = ^{ puts("In OC empty block..."); };
MyClassOC * myClassOC = [[MyClassOC alloc] init];
[myClassOC invoke:block];

然而,invoke...中的Objective-C代码无法调用通过id传入的Swift闭包:

let myClassOC = MyClassOC()

let myBlock : EmptyBlock = {
    print("In Swift EmptyBlock...")
}

myClassOC.invoke(myBlock)

我在这一行上以EXC_BAD_ACCESS结尾:

   EmptyBlock emptyBlock = blk;

知道这里发生了什么吗?

2 个答案:

答案 0 :(得分:3)

原因可能是Swift 3中引入的支持,任何Swift类型都被表示为Objective-C中id类型的不透明对象。有关详细信息,请阅读this Apple blog post中的 Objective-C中的Swift值类型。在这种情况下,当参数类型为_SwiftValue *时,您将看到Swift类型作为id传递,这是一种不透明的类型。

这种方法的好处是Swift值类型可以存储在Objective-C集合中;但缺点是您无法在Objective-C端转换为Objective-C兼容值。调试代码,您将看到该块作为_SwiftValue *而不是Objective-C块类型传递。

声明bak参数以EmptyBlock类型并且您的代码有效。

HTH

答案 1 :(得分:2)

因为Swift闭包和Objective-C块不是一回事。如果Swift看到您正在调用具有Objective-C块类型的方法,则会自动将闭包转换为Objective-C块,但是否则不会这样做。正如CRD的回答所提到的,在Swift 3中,任何Swift值都可以表示为Objective-C对象,所以即使它看到它需要一个Objective-C对象,它仍然不知道你想要一个Objective-C块,因为一个Swift闭包仍然可以桥接到一个不透明的对象。

我想通过Swift传递它的唯一方法就是:

myClassOC.invoke(myBlock as @convention(block) () -> Void)