如何调用一个Objective-c函数,它接受来自Swift的块字典作为参数?

时间:2017-09-14 17:07:05

标签: objective-c swift closures objective-c-blocks

我的目标c文件中有一个函数(比方说MyBlockExecutor类):

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

现在,我想从Swift调用此函数。这是我的快速电话:

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

这会导致我的应用崩溃。我在这一行收到EXC_BAD_ACCESS错误:

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

虽然从Objective-C调用相同的函数可以很好地工作。 另外,我已将MyBlock定义为:

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

如何解决此问题?

编辑: 我对目标c函数的更改持开放态度,我只是需要将一组闭包从swift传递给我的目标c函数并运行块。

2 个答案:

答案 0 :(得分:2)

您可以使用与Swift blocks not working中类似的方法:使用@convention(block)注释块 使用Objective-C块调用约定和(显式)强制转换 在将它放入字典之前将其发送到AnyObject

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

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

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

这在我的测试中按预期工作。

它也与Quinn“The Eskimo!”中提到的相似 the Apple developer forum作为一种方法 传递一个闭包(在Swift中定义)作为Objective-C兼容 通过指针对象,只是我替换了unsafeBitCast 更简单的as AnyObject

您也可以内联编写所有内容:

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

或定义辅助函数:

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

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

答案 1 :(得分:0)

尝试破坏段中的代码并检查错误发生的位置..尽管它与您所做的几乎完全相同,但我们只是将代码分解为多行以便轻松调试

> 2017-09-14 15:37:21,186 ERROR [main] 
> o.a.coyote.http11.Http11NioProtocol Failed to start end point
> associated with ProtocolHandler ["https-jsse-nio-443"]
> java.lang.IllegalArgumentException:
> java.security.UnrecoverableKeyException: Cannot recover key
>             at org.apache.tomcat.util.net.AbstractJsseEndpoint.createSSLContext(AbstractJsseEndpoint.java:114)
>             at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.addPreviouslyRemovedConnectors(TomcatEmbeddedServletContainer.java:250)
>             at org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:193)
>             at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.startEmbeddedServletContainer(EmbeddedWebApplicationContext.java:297)
>             at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:145)
>             at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
>             at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
>             at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
>             at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
>             at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
>             at com.atlassian.bitbucket.internal.boot.BitbucketServerApplication.start(BitbucketServerApplication.java:247)
>             at com.atlassian.bitbucket.internal.boot.BitbucketServerApplication.main(BitbucketServerApplication.java:83)
>             at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
>             at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
>             at com.atlassian.bitbucket.internal.launcher.BitbucketServerLauncher.start(BitbucketServerLauncher.java:151)
>             at com.atlassian.bitbucket.internal.launcher.BitbucketServerLauncher.main(BitbucketServerLauncher.java:99)
>             ... 11 frames trimmed