arrays - 如何正确调用 CFBinaryHeapGetValues() 并在 Swift 中解析生成的 C 数组?

标签 arrays swift core-foundation binary-heap

任何人都可以阐明如何在 Swift 中对 Core Foundation 的 CFBinaryHeap 数据结构调用 CFBinaryHeapGetValues 吗?代码示例/示例对我有很大帮助。这是我目前的代码:

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> s,不是 UnsafePointer<[CountedColor]> .

elementPtr.pointee检索第一个 UnsafeMutablePointer<CountedColor> ,所以你的两个print语句有效,但这并不意味着 returnedValues您的代码包含您期望的内容。

处理 C 数组的一个好方法是创建一个 UnsafeBufferPointer .

let returnedValues = UnsafeBufferPointer(start: elementPtr, count: 2)

UnsafeBufferPointer用作 Collection 类型,因此您可以轻松地将其转换为 Array .

let values = returnedValues.map{$0!.load(as: CountedColor.self)}

请不要忘记 deinitializedeallocate allocate编辑区域。

要做到这一点,您需要保留所有分配的指针及其计数。因此,您可能需要将代码的某些部分更改为:

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)

您可能想要添加任意数量的 CountedColor s 到 CFBinaryHeap,在这种情况下,您可以这样写:

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)
}

(您可能需要将我代码中所有 count:capacity:2 更改为 colors.count 。)

然后..

elementPtr.deinitialize(count: colors.count)
elementPtr.deallocate(capacity: colors.count)
fooPointer.deinitialize(count: colors.count)
fooPointer.deallocate(capacity: colors.count)

无论如何,匹配initializedeinitialize , allocatedeallocate .

你可能会在 Swift 标准库中找到一些有用的类型来改进代码,但一次学习太多可能不是一个好习惯......

关于arrays - 如何正确调用 CFBinaryHeapGetValues() 并在 Swift 中解析生成的 C 数组?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40966912/

相关文章:

javascript - 更新 meteor 集合中的子数组列表

相当于 mysql char 的 JavaScript

xcode - 将异步获取的数据传递给另一个类

iphone - iOS 中的本地 Assets URL 有多独特

swift - CFError 到 NSError 的免费桥接在 Swift 3 中不起作用

c# - 有没有更有效的方法来禁用多个脚本,同时在 Unity 中的单个游戏对象上保持一些事件?

c - c中的json数组解析

ios - 如何将值从一个函数传递到另一个函数?

在 ForEach 中删除数组元素时,SwiftUI 没有索引

macos - 测量 OS X 中耗时