您要包装的 Objective-C 代码中有一个错误。
编写 Objective-C 接口的人非常友好地添加了泛型注释,以便您获得更多有用的类型信息。
如果没有泛型(和可空性)注释,有人可能会这样懒惰地编写接口:
+ (NSDictionary *)stringsDictionary;
这很烦人,因为在 Swift 中,这很不方便最终得到以下签名:
class func stringsDictionary() -> [NSObject : AnyObject]!
但是在 Objective-C 代码中添加了泛型和可空性注释,如下所示:
+ (NSDictionary<NSString *, NSArray<NSString *> *> * _Nonnull)stringsDictionary;
我们最终得到了一个更容易理解的 Swift 签名:
class func stringsDictionary() -> [String: [String]]
很明显,这是一个以字符串键和字符串数组为值的字典。
但在 Objective-C 代码中并未严格执行 Objective-C 注释。它们只是建议。在我们桥接到 Swift 方面之前,这些泛型注释不会严格执行。
所以在通过应用这些Objective-C泛型注释进行桥接的过程中,Swift会通过您问题中提到的方法进行调用:
_convertNSDictionaryToDictionary<A, B where ...> (NSDictionary?) -> [A : B]
这就是将 Objective-C 中的 NSDictionary 转换为 Swift 字典的方式。但是,如果字典的 实际 内容与泛型签名(以及 Swift 因此推断类型的类型)不匹配,您将遇到此崩溃。
实际上,此崩溃大致相当于您尝试使用 as! 运算符但转换失败时遇到的崩溃。
例如:
let someDict: AnyObject = ["foo": ["a", "b", "c"], "bar": [1, 2, 3]]
let stringDict = someDict as! [String: [String]]
此转换将失败并且您的应用程序将崩溃,因为 someDict 不能解释为 [String: [String]]。充其量只能解释为[String: [Any]]。
因此,例如,如果该 Objective-C 方法的实现如下所示:
+ (NSDictionary<NSString *, NSArray<NSString *> *> * _Nonnull)stringsDictionary {
return @{ @"foo": @[@"foo1", @"foo2", @"foo3", @"foo4", @"foo5"],
@"bar": @[@1, @2, @3, @4, @5],
@"baz": @[@"baz1", @"baz2", @"baz3", @"baz4", @"baz5"] };
}
尝试从 Swift 调用此方法会生成确切的错误消息,因为键 @"bar" 的值是 NSNumber 对象的数组,所以充其量可以将其视为 [String: [Any]]在 Swift 中。
我们可以像这样修复 Objective-C 泛型注解:
+ (NSDictionary<NSString *, NSArray *> * _Nonnull)stringsDictionary;
或者,我们可以修复实现并删除看似错误的值(因为该方法称为 stringsDictionary 和 NSNumber 值显然不是字符串)。