2016-12-05 16 views
1

Может ли кто-нибудь пролить свет на то, как вызвать CFBinaryHeapGetValues ​​в структуре данных CFBinaryHeap Core Foundation в Swift? Пример кода/пример очень помог бы мне. Это код, у меня в данный момент:Как правильно вызвать CFBinaryHeapGetValues ​​() и проанализировать полученный массив C в Swift?

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. Таким образом, это означает, что массив, который я передаю, обновляется со значениями, однако, когда я пытаюсь получить дескриптор на массиве, обращаясь к пунктам, делаются неудобными. Сначала я подумал, что это была вещь управления памятью, но после прочтения того, как работает мост в Swift, я не уверен, что это так, потому что это аннотированный API CoreFoundation.

ответ

1

В коде:

CFBinaryHeapAddValue(bh, fooPointer) 

Вы пропускание UnsafeMutablePointer<CountedColor>, два раза.

(количество элементов влияет на многие части.)

Таким образом, при вызове CFBinaryHeapGetValues(bh, elementPtr), вам необходимо зарезервировать область для C-массива, который может содержать два 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 работает как тип коллекции, так что вы можете легко преобразовать его в Array.

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

И, пожалуйста, не забудьте deinitialize и deallocate в 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 с к 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: s или capacity: s с 2 в моем коде на 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 стандартной библиотеки для улучшения кода, но слишком много, что в свое время не может быть хорошей привычкой, чтобы узнать ...

+0

Я upvoted и принял ваш ответ, спасибо так много! Теперь я понимаю это намного лучше. Один быстрый последующий вопрос. В этой последней строке кода, который вы написали, как работает эта функция (как :)? Насколько я понимаю, это просто указатель void *. Как тогда Swift возможно знает, как читать эти необработанные байты и создавать объект CountedColor. Я прочитал документацию, и это все равно не помогло. Мне кажется, что это волшебство для меня, честно говоря, из мира objC, C++ и C, много взаимодействий Swift с C кажется для меня как-то волшебным :( – AyBayBay

+0

@AyBayBay, так ли эта загрузка (как :) работает? _ вы будете использовать 'void *' для соответствующего типа при получении некоторого содержимого в C, например '* ((SomeType *) aVoidPointer)'. В Swift 'aVoidPointer.load (as: SomeType.self)' является его эквивалентом Или вы можете считать это как кратчайшую форму 'aVoidPointer.assumingMemoryBound (to: SomeType.self) .pointee'. – OOPer

+0

Хорошо, спасибо :) Последний вопрос, который я хотел бы рассмотреть, я хотел бы рассмотреть объекты, которые мне нужны освободить/освободить. Глядя на код, я бы предположил, что это будет callbackPtr, каждый отдельный объект UnsafeMutablePointer , который я добавил в BinaryHeap, и, наконец, elementPtr и его объекты внутри. Правильно ли я это понимаю? – AyBayBay