【问题标题】:What is _convertNSDictionaryToDictionary<A, B where ...> (NSDictionary?) -> [A : B] and why is it crashing my app?什么是 _convertNSDictionaryToDictionary<A, B where ...> (NSDictionary?) -> [A : B] 为什么它会导致我的应用程序崩溃?
【发布时间】:2016-04-07 01:35:29
【问题描述】:

我有以下 Swift 代码:

class ThingChecker {
    static func checkThing() -> [String: [String]] {
        return Thing.stringsDictionary()
    }
}

Thing 是一个在 Objective-C 中实现的类,具有以下接口:

@interface Thing : NSObject

+ (NSDictionary<NSString *, NSArray<NSString *> *> * _Nonnull)stringsDictionary;

@end

但是当我运行我的应用并调用 ThingChecker.checkThing() 时,我的应用崩溃并出现以下错误:

EXC_BAD_INSTRUCTION(代码=EXC_i386_INVOP,子代码 0x0)

控制台中没有任何有用的打印。它只显示(lldb)

在调试器的堆栈跟踪中可以找到唯一看起来非常有用的东西。我可以在堆栈中看到两个帧,这可能会提供一些线索。首先,在我的代码下面是:

_convertNSDictionaryToDictionary<A, B where ...> (NSDictionary?) -> [A : B]

就在那下面,我看到了:

_arrayForceCast<A, B> ([A]) -> [B]

但是点击其中任何一个都只是指向汇编代码。

这里发生了什么?是什么导致了这次崩溃?我完全被难住了。

【问题讨论】:

    标签: objective-c swift generics


    【解决方案1】:

    您要包装的 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;
    

    或者,我们可以修复实现并删除看似错误的值(因为该方法称为 stringsDictionaryNSNumber 值显然不是字符串)。

    【讨论】:

      猜你喜欢
      • 2014-03-29
      • 2022-12-12
      • 2010-12-08
      • 2019-08-07
      • 2021-10-06
      • 2011-05-30
      • 2019-10-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多