web-dev-qa-db-ja.com

CFArrayをSwift Arrayに変換する方法は?

Appleの「Swift with Cocoa and Objective-C」の使用」によると、「Swiftでは、フリーダイヤルのブリッジされたFoundationタイプとCoreFoundationタイプの各ペアを交換可能に使用できます」。これによりCoreでの作業が可能になります。ファンデーションは実際よりもずっとシンプルに聞こえます...

CoreTextから返されるCFArrayを操作しようとしています。私はこのコードを持っています:

let lines: CFArrayRef  = CTFrameGetLines(frame)

この配列のメンバーにアクセスするには、2つの方法が考えられます。どちらも今私のために働いていません。


方法1-CFArrayを直接使用する

let line: CTLineRef = CFArrayGetValueAtIndex(lines, 0)

これにより、「 'ConstUnsafePointer <()>' in not convertible to'CTLineRef '」というエラーが発生します。キャストしてもこのエラーは変わらないようです。

同様に、行を「交換可能に」Swift配列として使用できるようにしたいと思います。ただし、

let line: CTLineRef = lines[0]

「 'CFArrayRef'には 'subscript'という名前のメンバーがありません」というエラーが発生します


方法#2-CFArrayをSwift配列に変換する

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

ここで、Swift配列を宣言し、ブリッジされたCFArrayと等しくなるように設定しました。これはエラーなしでコンパイルされますが、実行すると、2行目でEXC_BREAKPOINTがクラッシュします。おそらく私はこれでSwift言語を正しく使用していません...

23
user1021430

SwiftコンパイラとSwiftドキュメントの現在の状態に基づいて、これを行う方法は次のとおりです。これが後のベータ版でクリーンアップされることを願っています。

更新:ベータ5以降、reinterpretCastはunsafeBitCastに名前が変更され、CTLineオブジェクトを入力として送信する必要があります。方法2はまだ機能しません。


方法1-CFArrayを直接使用する

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

Gary Makinのコメントについて-RefはCTLineRefから削除できますが、これによってARCと非ARCは変わりません。 CocoaおよびObjective-Cの53〜54ページでのSwiftの使用)によると、refとnon-refはコンパイラと同じです。CFReleaseを呼び出そうとすると、コンパイラエラーが発生します。


方法2-CFArrayをa Swift array-に変換します現在機能しません

理想的には、行をCTLineオブジェクトのSwift配列に変換する必要があります。これは、CTFrameGetLinesによって返されるものであり、変換後の型の安全性を提供するためです。おそらくコンパイラのバグが原因で、配列[AnyObject]配列に変換できますが、[CTLine]には変換できません。 Appleのドキュメント によると、これは機能するはずです。

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がクラッシュします。最後の行。

10
user1021430

どうやらSwift 2では、CFArray as [AnyObject]をキャストできます。

Swift 1.2から変換した後にコードが機能しなくなった理由を理解するのに多くの時間を費やしました。私の場合、一部のAPIがCFArray!からCFArray?に変更され、このキャストはnilを返していました。 :

let cfArray = ... // Function that returns CFArray? instead of CFArray!
if let array = cfArray as? [AnyObject] { // nil
...

オプションでオプションから非オプションにキャストしているという警告はありませんが、これは意味がありません。

7
pointum

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/Xcode8.3.3およびSwift 4.0/Xcode 9でテスト済み)。

4
Martin R

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
Marco Luglio

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

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

もっと良い方法があるといいのですが。

1
kas-kad