如何正确调用 CFBinaryHeapGetValues() 并在 Swift 中解析生成的 C 数组?
How to properly call CFBinaryHeapGetValues() and parse the resulting C Array in Swift?
任何人都可以阐明如何在 Swift 中对 Core Foundation 的 CFBinaryHeap 数据结构调用 CFBinaryHeapGetValues 吗?代码 sample/example 会对我有很大帮助。这是我目前的代码:
public class CountedColor : NSObject
{
public private(set) var count: Int
public private(set) var color: UIColor
public init(color: UIColor, colorCount: Int)
{
self.count = colorCount
self.color = color
super.init()
}
}
var callbacks = CFBinaryHeapCallBacks()
callbacks.compare = { (a,b,unused) in
let afoo : CountedColor = (a?.assumingMemoryBound(to: CountedColor.self).pointee)!
let bfoo : CountedColor = (b?.assumingMemoryBound(to: CountedColor.self).pointee)!
if ( afoo.count == bfoo.count ) { return CFComparisonResult.compareEqualTo }
if ( afoo.count > bfoo.count ) { return CFComparisonResult.compareGreaterThan }
return CFComparisonResult.compareLessThan
}
let callbackPointer = UnsafeMutablePointer<CFBinaryHeapCallBacks>.allocate(capacity: 1)
callbackPointer.initialize(to: callbacks)
var bh = CFBinaryHeapCreate(nil, 0, callbackPointer, nil)
var fooPointer : UnsafeMutablePointer<CountedColor>!
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
fooPointer.initialize(to: CountedColor(color: UIColor.blue, colorCount: 72))
CFBinaryHeapAddValue(bh, fooPointer)
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
fooPointer.initialize(to: CountedColor(color: UIColor.red, colorCount: 99))
CFBinaryHeapAddValue(bh, fooPointer)
var elements = [CountedColor](repeating: CountedColor(color: UIColor.white, colorCount: 0), count: 2)
var elementPtr = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 1)
elementPtr.initialize(to: elements)
CFBinaryHeapGetValues(bh, elementPtr)
var returnedValues: UnsafePointer<[CountedColor]> = (elementPtr.pointee?.assumingMemoryBound(to: [CountedColor].self))!
print(elementPtr.pointee?.assumingMemoryBound(to: CountedColor.self).pointee.count)
print(elementPtr.pointee?.assumingMemoryBound(to: CountedColor.self).pointee.color)
let values = returnedValues.pointee
^^如果您查看上面的最后三行,代码在我得到 EXC_BAD_ACCESS 的最后一行失败。我很困惑为什么因为上面的两个打印语句确实有效。我能够验证函数返回的 C 数组中的第一个对象确实是计数为 72 的较小颜色。所以这意味着我传入的数组正在用值更新,但是当我尝试获取句柄时在阵列上通过访问 pointee 事情变得一团糟。起初我认为这是一个内存管理问题,但在阅读 Swift 中桥接的工作原理后,我不确定情况是否如此,尤其是因为这是一个带注释的 CoreFoundation API.
在您的代码中:
CFBinaryHeapAddValue(bh, fooPointer)
您两次通过 UnsafeMutablePointer<CountedColor>
。
(元素数量影响很多部分。)
所以,调用CFBinaryHeapGetValues(bh, elementPtr)
时,需要为C-array预留一个区域,可以包含两个UnsafeMutablePointer<CountedColor>
。
你应该写成:
var elementPtr = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 2) //<- You need to allocate a region for two elements
elementPtr.initialize(to: nil, count: 2) //<- The content is overwritten, initial values can be nil
CFBinaryHeapGetValues(bh, elementPtr)
还有这段代码:
var returnedValues: UnsafePointer<[CountedColor]> = (elementPtr.pointee?.assumingMemoryBound(to: [CountedColor].self))!
正如我上面写的,elementPtr
指向的区域中存储的结果是UnsafeMutablePointer<CountedColor>
,而不是UnsafePointer<[CountedColor]>
。
elementPtr.pointee
检索第一个 UnsafeMutablePointer<CountedColor>
,因此您的两个 print
语句有效,但这并不意味着您的代码 returnedValues
包含您期望的内容。
处理 C 数组的一个好方法是创建 UnsafeBufferPointer
.
let returnedValues = UnsafeBufferPointer(start: elementPtr, count: 2)
UnsafeBufferPointer
用作集合类型,因此您可以轻松地将其转换为 Array
.
let values = returnedValues.map{[=14=]!.load(as: CountedColor.self)}
请不要忘记 deinitialize
和 deallocate
allocate
ed 区域。
要做到这一点,您需要保留所有分配的指针及其计数。因此,您可能需要将部分代码更改为:
var fooPointer : UnsafeMutablePointer<CountedColor>!
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
fooPointer.initialize(to: CountedColor(color: UIColor.blue, colorCount: 72))
CFBinaryHeapAddValue(bh, fooPointer)
var barPointer : UnsafeMutablePointer<CountedColor>!
barPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
barPointer.initialize(to: CountedColor(color: UIColor.red, colorCount: 99))
CFBinaryHeapAddValue(bh, barPointer)
然后在使用完您的 CFBinaryHeap (bh
)...
elementPtr.deinitialize(count: 2)
elementPtr.deallocate(capacity: 2)
barPointer.deinitialize()
barPointer.deallocate(capacity: 1)
fooPointer.deinitialize()
fooPointer.deallocate(capacity: 1)
你可能想向 CFBinaryHeap 添加任意数量的 CountedColor
s,在这种情况下,你可以这样写:
let colors = [
CountedColor(color: UIColor.blue, colorCount: 72),
CountedColor(color: UIColor.red, colorCount: 99),
//...
]
var fooPointer : UnsafeMutablePointer<CountedColor>!
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: colors.count)
fooPointer.initialize(from: colors)
for i in colors.indices {
CFBinaryHeapAddValue(bh, fooPointer+i)
}
(您可能需要将我的代码中具有 2
的所有 count:
或 capacity:
更改为 colors.count
。)
然后..
elementPtr.deinitialize(count: colors.count)
elementPtr.deallocate(capacity: colors.count)
fooPointer.deinitialize(count: colors.count)
fooPointer.deallocate(capacity: colors.count)
无论如何,匹配 initialize
和 deinitialize
、allocate
和 deallocate
。
您可能会在 Swift 标准库中找到一些有用的类型来改进代码,但是一次学习太多可能不是一个好习惯...
任何人都可以阐明如何在 Swift 中对 Core Foundation 的 CFBinaryHeap 数据结构调用 CFBinaryHeapGetValues 吗?代码 sample/example 会对我有很大帮助。这是我目前的代码:
public class CountedColor : NSObject
{
public private(set) var count: Int
public private(set) var color: UIColor
public init(color: UIColor, colorCount: Int)
{
self.count = colorCount
self.color = color
super.init()
}
}
var callbacks = CFBinaryHeapCallBacks()
callbacks.compare = { (a,b,unused) in
let afoo : CountedColor = (a?.assumingMemoryBound(to: CountedColor.self).pointee)!
let bfoo : CountedColor = (b?.assumingMemoryBound(to: CountedColor.self).pointee)!
if ( afoo.count == bfoo.count ) { return CFComparisonResult.compareEqualTo }
if ( afoo.count > bfoo.count ) { return CFComparisonResult.compareGreaterThan }
return CFComparisonResult.compareLessThan
}
let callbackPointer = UnsafeMutablePointer<CFBinaryHeapCallBacks>.allocate(capacity: 1)
callbackPointer.initialize(to: callbacks)
var bh = CFBinaryHeapCreate(nil, 0, callbackPointer, nil)
var fooPointer : UnsafeMutablePointer<CountedColor>!
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
fooPointer.initialize(to: CountedColor(color: UIColor.blue, colorCount: 72))
CFBinaryHeapAddValue(bh, fooPointer)
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
fooPointer.initialize(to: CountedColor(color: UIColor.red, colorCount: 99))
CFBinaryHeapAddValue(bh, fooPointer)
var elements = [CountedColor](repeating: CountedColor(color: UIColor.white, colorCount: 0), count: 2)
var elementPtr = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 1)
elementPtr.initialize(to: elements)
CFBinaryHeapGetValues(bh, elementPtr)
var returnedValues: UnsafePointer<[CountedColor]> = (elementPtr.pointee?.assumingMemoryBound(to: [CountedColor].self))!
print(elementPtr.pointee?.assumingMemoryBound(to: CountedColor.self).pointee.count)
print(elementPtr.pointee?.assumingMemoryBound(to: CountedColor.self).pointee.color)
let values = returnedValues.pointee
^^如果您查看上面的最后三行,代码在我得到 EXC_BAD_ACCESS 的最后一行失败。我很困惑为什么因为上面的两个打印语句确实有效。我能够验证函数返回的 C 数组中的第一个对象确实是计数为 72 的较小颜色。所以这意味着我传入的数组正在用值更新,但是当我尝试获取句柄时在阵列上通过访问 pointee 事情变得一团糟。起初我认为这是一个内存管理问题,但在阅读 Swift 中桥接的工作原理后,我不确定情况是否如此,尤其是因为这是一个带注释的 CoreFoundation API.
在您的代码中:
CFBinaryHeapAddValue(bh, fooPointer)
您两次通过 UnsafeMutablePointer<CountedColor>
。
(元素数量影响很多部分。)
所以,调用CFBinaryHeapGetValues(bh, elementPtr)
时,需要为C-array预留一个区域,可以包含两个UnsafeMutablePointer<CountedColor>
。
你应该写成:
var elementPtr = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 2) //<- You need to allocate a region for two elements
elementPtr.initialize(to: nil, count: 2) //<- The content is overwritten, initial values can be nil
CFBinaryHeapGetValues(bh, elementPtr)
还有这段代码:
var returnedValues: UnsafePointer<[CountedColor]> = (elementPtr.pointee?.assumingMemoryBound(to: [CountedColor].self))!
正如我上面写的,elementPtr
指向的区域中存储的结果是UnsafeMutablePointer<CountedColor>
,而不是UnsafePointer<[CountedColor]>
。
elementPtr.pointee
检索第一个 UnsafeMutablePointer<CountedColor>
,因此您的两个 print
语句有效,但这并不意味着您的代码 returnedValues
包含您期望的内容。
处理 C 数组的一个好方法是创建 UnsafeBufferPointer
.
let returnedValues = UnsafeBufferPointer(start: elementPtr, count: 2)
UnsafeBufferPointer
用作集合类型,因此您可以轻松地将其转换为 Array
.
let values = returnedValues.map{[=14=]!.load(as: CountedColor.self)}
请不要忘记 deinitialize
和 deallocate
allocate
ed 区域。
要做到这一点,您需要保留所有分配的指针及其计数。因此,您可能需要将部分代码更改为:
var fooPointer : UnsafeMutablePointer<CountedColor>!
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
fooPointer.initialize(to: CountedColor(color: UIColor.blue, colorCount: 72))
CFBinaryHeapAddValue(bh, fooPointer)
var barPointer : UnsafeMutablePointer<CountedColor>!
barPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: 1)
barPointer.initialize(to: CountedColor(color: UIColor.red, colorCount: 99))
CFBinaryHeapAddValue(bh, barPointer)
然后在使用完您的 CFBinaryHeap (bh
)...
elementPtr.deinitialize(count: 2)
elementPtr.deallocate(capacity: 2)
barPointer.deinitialize()
barPointer.deallocate(capacity: 1)
fooPointer.deinitialize()
fooPointer.deallocate(capacity: 1)
你可能想向 CFBinaryHeap 添加任意数量的 CountedColor
s,在这种情况下,你可以这样写:
let colors = [
CountedColor(color: UIColor.blue, colorCount: 72),
CountedColor(color: UIColor.red, colorCount: 99),
//...
]
var fooPointer : UnsafeMutablePointer<CountedColor>!
fooPointer = UnsafeMutablePointer<CountedColor>.allocate(capacity: colors.count)
fooPointer.initialize(from: colors)
for i in colors.indices {
CFBinaryHeapAddValue(bh, fooPointer+i)
}
(您可能需要将我的代码中具有 2
的所有 count:
或 capacity:
更改为 colors.count
。)
然后..
elementPtr.deinitialize(count: colors.count)
elementPtr.deallocate(capacity: colors.count)
fooPointer.deinitialize(count: colors.count)
fooPointer.deallocate(capacity: colors.count)
无论如何,匹配 initialize
和 deinitialize
、allocate
和 deallocate
。
您可能会在 Swift 标准库中找到一些有用的类型来改进代码,但是一次学习太多可能不是一个好习惯...