访问 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 期望 CFArray
s 和 NSArray
s 只包含有效的对象和指针,并且它这样对待 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
) 的标签,这绝对不是一个有效的指针(但我没有其他领域知识验证这是否是您期望的标签)。
我的以下代码因 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 期望 CFArray
s 和 NSArray
s 只包含有效的对象和指针,并且它这样对待 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
) 的标签,这绝对不是一个有效的指针(但我没有其他领域知识验证这是否是您期望的标签)。