Reputation: 4891
This question has been inspired by this one: Swift closure in array becomes nil in Objective-c, which I answered with a workaround using a wrapper class, but I'm still curious as to why one can't call, in Objective-C, a Swift closure passed via id
. A simple example follows.
Objective-C code:
// 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
No problem providing an Objective-C block:
EmptyBlock block = ^{ puts("In OC empty block..."); };
MyClassOC * myClassOC = [[MyClassOC alloc] init];
[myClassOC invoke:block];
However, the Objective-C code in invoke...
can't call a Swift closure passed in via id
:
let myClassOC = MyClassOC()
let myBlock : EmptyBlock = {
print("In Swift EmptyBlock...")
}
myClassOC.invoke(myBlock)
I end up with EXC_BAD_ACCESS
on this line:
EmptyBlock emptyBlock = blk;
Any idea what's going on here?
Upvotes: 1
Views: 919
Reputation: 122518
Because Swift closures and Objective-C blocks are not the same things. Swift automatically converts a closure to an Objective-C block if they see that you are calling a method with an Objective-C block type, but it doesn't do that otherwise. And as CRD's answer mentions, in Swift 3 any Swift value can be represented as an Objective-C object, so even though it sees that it expects an Objective-C object, it still doesn't know you want an Objective-C block because a Swift closure can still be bridged to an opaque object.
The only way I figured out to pass it from Swift and work is something like:
myClassOC.invoke(myBlock as @convention(block) () -> Void)
Upvotes: 2
Reputation: 53010
The reason is probably the support introduced in Swift 3 for any Swift type to be represented as an opaque object of id
type in Objective-C. Read Swift Value Types in Objective-C in this Apple blog post for details. In that you will see that Swift types are passed as _SwiftValue *
when the parameter type is id
, this is an opaque type.
The argued benefit of this approach is that Swift value types can be stored in Objective-C collections; however the disadvantage is that you cannot convert to an Objective-C compatible value on the Objective-C side. Debug your code and you'll see the block is being passed as a _SwiftValue *
and not an Objective-C block type.
Declare the bak
parameter to have EmptyBlock
type and your code works.
HTH
Upvotes: 3