我有一个Swift对象,它获取块的字典(以字符串为键),存储它,并在稍后根据外部环境在给定的键下运行块(考虑根据后端响应的不同行为):

@objc func register(behaviors: [String: @convention(block) () -> Void] {
  // ...
}

它用于混合语言项目,因此需要能够从SWIFT和Objective-C访问.这就是为什么会有@convention(block),否则编译器会抱怨不能用Objective-C来表示这个函数.

它在SWIFT中运行良好.但当我试图从目标C调用它时,就像这样:

[behaviorManager register:@{
  @"default": ^{
    // ...
  }
}];

代码崩溃,我收到以下错误:

Could not cast value of type '__NSGlobalBlock__' (0x...) to '@convention(block) () -> ()' (0x...).

为什么,这是怎么回事?我认为@convention(block)是专门告诉编译器,目标C块将被传递,而这正是在调用中传递给函数的内容.

推荐答案

这就是为什么会有@convention(block)个,否则编译器会 抱怨无法在中表示此函数 目标-C

出于一致性的考虑:通常反过来使用@convention属性--当有一个接口接受一个C指针(并用C实现)或一个Objective-C块(用Objective-C实现),而您传递一个带有相应@convention作为参数的SWIFT闭包时(这样编译器实际上可以为C/Objective-C实现从SWIFT闭包中生成适当的内存布局).因此,如果它是客观C端,那么它应该可以很好地工作,在那里,Swift创建的闭包被称为类似块:

@interface TDWObject : NSObject

- (void)passArguments:(NSDictionary<NSString *, void(^)()> *)params;

@end

如果类向SWIFT公开,则编译器会生成相应的签名,该签名将获取一个包含@convention(block)个值的字典:

func passArguments(_ params: [String : @convention(block) () -> Void])

然而,这并不能抵消这样一个事实,即属性为@convention的闭包仍然可以在SWIFT中使用,但当涉及到集合时,事情就变得复杂了,而且它与SWIFT集合的值类型和引用类型的优化有关.为了说明这一点,我建议通过将其提升为[String: AnyObject]并稍后转换为对应的块类型来明确此集合持有引用类型:

@objc func takeClosures(_ closures: [String: AnyObject]) {
    guard let block = closures["One"] else {
        return // the block is missing
    }
    let closure = unsafeBitCast(block, to: ObjCBlock.self)
    closure()
}

或者,您可能希望将块包装在一个Objective-C对象中,以便SWIFT清楚地知道它是一个引用类型:

typedef void(^Block)();

@interface TDWBlockWrapper: NSObject

@property(nonatomic, readonly) Block block;

@end

@interface TDWBlockWrapper ()

- (instancetype)initWithBlock:(Block)block;

@end

@implementation TDWBlockWrapper

- (instancetype)initWithBlock:(Block)block {
    if (self = [super init]) {
        _block = block;
    }
    return self;
}

@end

然后,对于SWIFT来说,它的工作原理就是这么简单:

@objc func takeBlockWrappers(_ wrappers: [String: TDWBlockWrapper]) {
    guard let wrapper = wrappers["One"] else {
        return // the block is missing
    }
    wrapper.block()
}

Ios相关问答推荐

为什么Pushed View Controller在UIKit中具有导航控制器?

DriverKit驱动程序中可能存在IOBufferMemoyDescriptor泄漏

为什么EKEventStore().questFullAccessToEvents()可以与模拟器一起使用,而不能与真实设备一起使用?

SwiftUI中的Tap手势无法识别视图上的偏移变化

尽管存在浮点错误,如何显示正确的计算结果.计算器应用程序

NavigationLink 只能在文本部分点击

动画文本位置

expo EAS build (iOS) 在 Pod 安装步骤中失败 (SDK45 & 46)

如何在 SwiftUI 的 TabView 中添加底部曲线?

如何仅更改一个视图的导航标题 colored颜色 ?

SwiftUI Select 器值在超过最大值时重置

通过touch 传递到下面的 UIViews

如何在 iOS 的 xib 中设置十六进制 colored颜色 代码

什么是伞头?

判断从 JSON 字符串返回的 Objective-C 中的空值

UICollectionView flowLayout 没有正确包装单元格

AFNetworking 2.0 向 GET 请求添加标头

Swift:如何在设备旋转后刷新 UICollectionView 布局

如何删除UITableView中最后一个单元格的最后一个边框?

NSURL 到带有 XCTest 的测试包中的文件路径