行代码在 lldb `p` 中有效,代码在托管单元测试中失败

Line code works in lldb `p`, code fails in hosted unit test

我发现了很多关于 NSArray element failed to match the Swift Array Element type 错误的 post。但是,我仍然无法让它工作。 我怀疑问题与我创建的 objective C 框架绑定有关,而不是转换问题。其中,我不明白的一件事是为什么代码在 lldb p 指令中工作,而不是在主机 ios 应用程序中执行的单元测试(我无法测试它,因为代码需要iphone 模拟器中不可用的 B-LE)。

class onceFirstMuseConnected: IXNMuseListener {
    let museManager: IXNMuseManager
    let callback: (_ muse: IXNMuse) -> Void

    init(museManager: IXNMuseManager, callback: @escaping (_ muse: IXNMuse) -> Void) {
        self.museManager = museManager
        self.callback = callback
        museManager.setMuseListener(self)
        museManager.startListening()
    }

    func museListChanged() {
        let muses: [IXNMuse] = museManager.getMuses()
        guard muses.count > 0 else {
            return
        }
        let _ = muses.first!.getName() // <<<< `muses.first` fails as well as any other
                                       //      kind of array's item access with "Fatal
                                       //      error: NSArray element failed to match the
                                       //      Swift Array Element type"
    }
}

奇怪的是我能够使该行在 lldb 中工作,如下图所示。

编辑: 由于我有多个反对票,我希望能解释为什么 lldb 行在主执行崩溃时在注释中工作。如果是类型转换问题,我认为 lldb 行将不起作用。 None 我找到的相关问题的答案。我承认我是一个 swift/objc 新手,如果这不是与 objc-swift 框架映射过程相关的问题,可能有一些我不明白的明显问题。

getMuses 函数由 djinni 包装,使用我编写的模块映射将专有 objective C 框架绑定到 swift。这是我写的模块图。我还没有遇到其他函数绑定的问题(请参阅评论)。删除 [system] 属性不会触发任何其他警告。

module Muse [system] {
    header "Muse.framework/Headers/Muse.h"
    export *
}

djinni 生成的声明:

public func getMuses() -> [IXNMuse]

原始objc声明:

 - (nonnull NSArray<IXNMuse *> *)getMuses;

由于框架是专有的,因此定义不可用。

返回的指针不太可能为空或指出错误的内存地址,因为 lldb 对屏幕截图中显示的 getName 方法的调用不会给出正确的结果。

已编辑答案

Muse.framework 是一个静态框架。 XCTests 构建在嵌入在主机应用程序中的捆绑包中。在我的设置中,涉及 3 个目标:我开发的框架、用于测试的主机应用程序和 XCTest 包。

目标二进制文件中使用的静态框架嵌入符号[=​​55=]。

为了避免重复的符号问题,静态框架应该只 linked 到目标框架**,而不是 xctest bundle 或测试主机应用程序,它过渡 link 符号 link使用目标框架。

为了确保目标框架嵌入其静态 linked 框架依赖的所有符号,必须将 -force_load 标志后跟静态依赖路径添加到目标框架 linking 选项 (OTHER_LDFLAGS).

原答案

感谢 OOPer 在问题评论中寻求帮助。

由于我不明白的原因,我得到了 IXNMuse 的双重定义 class。 Swift 两个定义之间的冲突是我遇到 NSArray element failed to match the Swift Array Element type 错误的原因。

修复程序

当我执行以下过程时,问题消失了:

A. 在测试以下代码时

let muses: NSArray = museManager.getMuses() as NSArray
let muse: IXNMuse = muses.firstObject! as! IXNMuse

我在运行时收到

Could not cast value of type 'IXNMuse' (0x103cfa180) to 'IXNMuse' (0x103e965e0).

B. 我有两个 import Muse 语句,一个在我的测试文件中,一个在我的框架目标源文件之一中。 从测试文件中删除 import Muse 语句(带有相关代码)。这通过删除双重 IXNMuse 定义让测试通过。

C. 我从测试目标的 Link Binary with Libraries 中删除了 Muse.framework,因为它已经包含在我测试的框架目标中。我从测试文件中放回刚刚删除的 import Muse 语句(带有相关代码,因为我仍然需要它)。 即使在两个目标源文件中使用双 Muse 导入,测试仍然通过。

D. 我把 Muse.framework 放回我的测试目标的 Link Binary with Libraries (我相信两者都不是错误目标 link 具有三角依赖的相同框架)。就这样,我又回到了刚开始写题时的状态。问题没有再次出现,测试仍然通过。

结论

我仍然有点困惑为什么我一开始会遇到这个问题。有可能我当时的状态并不完全相同,但我在更改我不记得的 linking 配置时在此过程中做了一些事情。