从Objective-C调用Swift的Block集合方法时崩溃的原因分析
你这里的崩溃根源在于**@convention(block)的使用场景错配**,以及Swift与Objective-C在字典类型桥接时的类型处理差异:
@convention(block)的正确用法:这个修饰符是用来告诉Swift,某个闭包要按照Objective-C Block的调用约定编译,通常用于直接将Swift闭包传递给Objective-C的Block参数,或是Swift内部手动封装OC Block的场景。但你把它直接放在字典的Value类型上,会让Swift对该类型的校验变得异常严格——它期望的是显式标记为OC调用约定的Swift闭包,而非从OC桥接过来的Block对象。
字典的桥接冲突:从Objective-C传递
NSDictionary到Swift时,字典里的Block(__NSGlobalBlock__)会自动桥接成Swift的普通闭包(不带@convention(block)标记)。但你的方法参数要求的是[String: @convention(block) () -> Void],Swift会尝试把桥接后的普通闭包强制转换成带OC约定的闭包类型,这两种类型底层表示存在差异,直接转换就会触发崩溃。
有两种简单的修复方式,任选其一即可:
方案一:移除字典Value的@convention(block)修饰
直接修改Swift的方法定义,将参数类型改为普通闭包字典,Swift会自动处理与OC Block的桥接:
@objc func register(behaviors: [String: () -> Void]) { // 存储和执行逻辑保持不变,Swift会自动适配OC传入的Block }
方案二:用Objective-C的Block类型定义参数
如果需要更明确的类型约束,可以先在Objective-C中定义一个Block类型:
typedef void (^VoidBlock)(void);
然后在Swift中引用这个类型作为字典的Value:
@objc func register(behaviors: [String: VoidBlock]) { // 逻辑不变 }
这样OC调用时直接传VoidBlock类型的Block,Swift能正确识别,不会出现类型转换问题。
Swift与OC混编时,方法参数中的Block类型会自动完成桥接,不需要手动添加@convention(block)修饰——这个修饰符更多用于Swift内部需要直接操作OC Block指针的场景(比如底层C交互),而非普通的混编方法参数。
内容的提问来源于stack exchange,提问作者raven_raven

