访问 CFArray 导致崩溃 Swift

Accessing CFArray causes crash in Swift

我的以下代码因 EXC_BAD_ACCESS 而崩溃,我不明白为什么。我最初的理解是这种情况下的内存保留应该是自动的,但似乎我错了......也许有人可以提供帮助。谢谢!代码写在Swift5,运行在iOS15.2在XCode13.2.1.

转换为 NSArray 会导致问题...

let someFont = CGFont("Symbol" as CFString)!
if let cfTags: CFArray = someFont.tableTags {
   let nsTags = cfTags as NSArray
   print(nsTags.count) // Prints: 16
   let tag0 = nsTags[0] // CRASH: Thread 1: EXC_BAD_ACCESS (code=257, ...)
 }

或者,使用 CFArray-API 也会造成麻烦(崩溃消息是关于指针未对齐,但根本原因似乎也是访问错误,例如,如果我将 UInt32.self 替换为 UInt8.self,从而消除了对齐问题)。

let someFont = CGFont("Symbol" as CFString)!
if let cfTags: CFArray = someFont.tableTags {
     print(CFArrayGetCount(cfTags)) // Prints: 16
     let tag0Ptr: UnsafeRawPointer = CFArrayGetValueAtIndex(cfTags, 0)!
     tag0Ptr.load(as: UInt32.self)// CRASH :Thread 1: Fatal error: load from misaligned raw pointer
}

这里的问题是 CGFont API 在 table 标签的存储中使用了一些高级的 C-ism,这实际上并没有转化为 Swift : CGFontCopyTableTags() returns a CFArrayRef 实际上不包含对象,而是整数。 (这在技术上是通过 CFArray 的接口允许的:它接受 C 中的 void *s,你可以在技术上将任何适合指针的整数填充到其中,即使指针值是无意义的...... ) Swift 期望 CFArrays 和 NSArrays 只包含有效的对象和指针,并且它这样对待 return 值——这就是通过 [=18 访问的原因=] 也失败了(Swift 需要一个对象,但值不是对象,所以它不能像指针一样访问,也不能保留,或者运行时可能期望做的任何事情)。

您的第二个代码片段更接近于您访问该值所需的方式:CFArrayGetValueAtIndex 看起来 return 一个指针,但您返回的值不是真实的指针——它实际上是一个整数直接存储在数组中,伪装成一个指针。

相当于

CGFontCopyTableTags文档中的Obj-C示例
tag = (uint32_t)(uintptr_t)CFArrayGetValue(table, k);

会是

let tag = unsafeBitCast(CFArrayGetValueAtIndex(cfTags, 0), to: UInt.self)

(请注意,您需要转换为 UInt 而不是 UInt32,因为 unsafeBitCast 要求输入值和输出类型具有相同的对齐方式。)

在我的模拟器中,我看到一个值为 1196643650 (0x47535542) 的标签,这绝对不是一个有效的指针(但我没有其他领域知识验证这是否是您期望的标签)。