如何将CFArray转换为Swift数组?

时间:2014-07-10 19:29:13

标签: nsarray swift core-foundation

根据Apple"使用Swift与Cocoa和Objective-C","在Swift中,您可以互换使用每对免费桥接基础和Core Foundation类型&# 34 ;.这使得使用Core Foundation的声音比实际更简单......

我正在尝试使用从CoreText返回的CFArray。我有这段代码:

let lines: CFArrayRef  = CTFrameGetLines(frame)

我看到两种可能的方法来访问此阵列的成员。现在也没有为我工作。


方式#1 - 直接使用CFArray

let line: CTLineRef = CFArrayGetValueAtIndex(lines, 0)

这会产生错误"' ConstUnsafePointer<()>'不能转换为CTLineRef'"。施放似乎不会改变此错误。

同样,我很乐意使用线条和#34;互换"像Swift数组一样,它说我可以。然而,

let line: CTLineRef = lines[0]

产生错误"' CFArrayRef'没有名为'下标'"

的成员

方式#2 - 将CFArray转换为Swift数组

var linesArray: Array = [CTLineRef]()
linesArray = bridgeFromObjectiveC(lines, linesArray.dynamicType)

在这里,我声明了一个Swift数组并将其设置为等同于桥接的CFArray。这个编译没有错误,但是当我运行它时,我在第二行遇到EXC_BREAKPOINT崩溃。也许我没有在这个上正确使用Swift语言...

5 个答案:

答案 0 :(得分:10)

以下是基于Swift编译器和Swift文档的当前状态的方法。希望这会在以后的测试版中得到清理。

更新:自Beta 5以来,reinterpretCast已重命名为unsafeBitCast,并且必须将CTLine对象作为输入发送给它。方式#2仍然不起作用。


方式#1 - 直接使用CFArray

let line: CTLine = reinterpretCast(CFArrayGetValueAtIndex(lines, 0))

关于Gary Makin的评论 - Ref可以从CTLineRef中删除,但这不会改变ARC与非ARC。根据Using Swift with Cocoa和Objective-C第53-54页,ref和non-ref与编译器相同。尝试调用CFRelease会导致编译器错误。


方式#2 - 将CFArray转换为Swift数组 - 目前无效

理想情况下,我们希望将行转换为CTLine对象的Swift数组,因为我们知道CTFrameGetLines返回的内容,在转换后为我们提供了类型安全性。可能由于编译器错误,数组可以转换为[AnyObject]数组,但不能转换为[CTLine]。根据{{​​3}},这应该有效:

let linesNS: NSArray  = CTFrameGetLines(frame)
let linesAO: [AnyObject] = linesNS as [AnyObject]
let lines: [CTLine] = linesAO as [CTLine]

将CFArray转换为NSArray,然后将NSArray转换为Swift Array [AnyObject],然后将该数组转换为特定类型CTLine。这会编译,但运行时,最后一行会出现EXC_BREAKPOINT崩溃。

答案 1 :(得分:3)

基于purrrminator的回答:

extension CFArray: SequenceType {
    public func generate() -> AnyGenerator<AnyObject> {
        var index = -1
        let maxIndex = CFArrayGetCount(self)
        return anyGenerator{
            guard ++index < maxIndex else {
                return nil
            }
            let unmanagedObject: UnsafePointer<Void> = CFArrayGetValueAtIndex(self, index)
            let rec = unsafeBitCast(unmanagedObject, AnyObject.self)
            return rec
        }
    }
}

let cfarray = giveMeArray()

for entry in cfarray {
    print(entry)
}

答案 2 :(得分:3)

刚试过XCode 7.3.1(Swift 2.2.1)

let cfElements = IOHIDDeviceCopyMatchingElements(self.device, nil, IOOptionBits(kIOHIDOptionsTypeNone)).takeUnretainedValue();
let nsElements:NSArray = cfElements
let elements:Array<IOHIDElement> = nsElements as! Array<IOHIDElement>

for element in elements { /**/ }

我不确定是否有必要转播给NSArray,但现在这样做对我来说

PS:也适用于.takeRetainedValue

答案 3 :(得分:2)

从Swift 3开始,CFArray可以直接桥接到[CTLine]

let lines = CTFrameGetLines(frame) as! [CTLine]
guard let firstLine = lines.first else {
    return // no line
}

以同样的方式:

let runs = CTLineGetGlyphRuns(firstLine) as! [CTRun]
guard let firstRun = runs.first else {
    return // no run
}

(使用Swift 3.1 / Xcode 8.3.3和Swift 4.0 / Xcode 9测试)。

答案 4 :(得分:1)

在swift 2.0中,我使用以下代码完成了从CFArray到Array的转换:

extension Array {
    static func fromCFArray(records : CFArray?) -> Array<Element>? {
        var result: [Element]?
        if let records = records {
            for i in 0..<CFArrayGetCount(records) {
                let unmanagedObject: UnsafePointer<Void> = CFArrayGetValueAtIndex(records, i)
                let rec: Element = unsafeBitCast(unmanagedObject, Element.self)
                if (result == nil){
                    result = [Element]()
                }
                result!.append(rec)
            }
        }
        return result
    }
}

我希望有更好的方法。