2

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.

user2473992
  • 117
  • 6

2 Answers2

4

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")
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • That worked! Also, can you tell me how to do this inline i.e., I don't want to create myBlock and dict variables. – user2473992 Sep 15 '17 at 05:53
0

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")
Er. Khatri
  • 1,384
  • 11
  • 29