How to call an objective-c function which accepts

2019-02-15 15:20发布

问题:

I have a function in my objective c file (lets say class MyBlockExecutor):

+ (void) runBlockFromDictionary: (NSDictionary*) blocksDict andKey: (NSString*) key
{
    if ( [blocksDict objectForKey: key] != nil )
    {
         ((MyBlock)[blocksDict objectForKey: key])();
    }
}

Now, I want to call this function from Swift. Here is my swift call:

MyBlockExecutor.runBlock(from: [
        "key1":{ ()->Void in
                    print("block for key1 called")
               }
        ], andKey: "key1")

This crashes my app. I am getting EXC_BAD_ACCESS error on this line:

((MyBlock)[blocksDict objectForKey: key])();

Although, calling the same function from Objective-C works perfectly fine. Also, I've defined MyBlock as :

typedef void (^MyBlock)(); //defined in MyBlockExecutor.h file

How do I resolve this?

Edit: I am open to changes in the objective c function, I just somehow need to pass a collection of closures from swift to my objective c function and run the block.

回答1:

You can use a similar approach as in Swift blocks not working: Annotate the block with @convention(block) to use the Objective-C block calling convention, and (explicitly) cast it to AnyObject before putting it into the dictionary:

let myBlock: @convention(block) () -> Void = {
    print("block for key1 called")
}

let dict = ["key1": myBlock as AnyObject]

MyBlockExecutor.runBlock(from: dict, andKey: "key1")

This worked as expected in my test.

It is also similar to what Quinn “The Eskimo!” suggested in the Apple developer forum as a method to pass a closure (defined in Swift) as an Objective-C compatible object through pointers, only that I replaced the unsafeBitCast by the simpler as AnyObject.

You can also write everything inline:

MyBlockExecutor.runBlock(from: ["key1": {
        print("block for key1 called")
    } as @convention(block) () -> Void as AnyObject
    ], andKey: "key1")

or define a helper function:

func objcBlock(from block: @convention(block) () -> Void) -> AnyObject {
    return block as AnyObject
}

MyBlockExecutor.runBlock(from: ["key1": objcBlock {
        print("block for key1 called")
    }], andKey: "key1")


回答2:

try to break the code in segments and check from where the error is coming.. although its nearly same what you have done we have just break the code in multiple line for debugging easily

//1. create the block instance separately
let myBlockForKey1:MyBlock = { () in
    print("block for key1 called")
}
//2. create dic of blocks as
let dicOfBlocks:[String:MyBlock] = ["key1":myBlockForKey1]
//3. call your function
MyBlockExecutor.runBlock(from: dicOfBlocks, andKey: "key1")