nsarray - array xcode swift
¿Cómo convertir CFArray a Swift Array? (6)
A partir de Swift 3, CFArray
puede conectar a [CTLine]
directamente:
let lines = CTFrameGetLines(frame) as! [CTLine]
guard let firstLine = lines.first else {
return // no line
}
Y de la misma manera:
let runs = CTLineGetGlyphRuns(firstLine) as! [CTRun]
guard let firstRun = runs.first else {
return // no run
}
(probado con Swift 3.1 / Xcode 8.3.3 y Swift 4.0 / Xcode 9).
De acuerdo con "Uso de Swift con Cocoa y Objective-C de Apple", "En Swift, puedes usar indistintamente cada par de tipos de Foundation y Core Foundation sin cargo de forma gratuita". Esto hace que trabajar con Core Foundation parezca más simple de lo que realmente es ...
Estoy tratando de trabajar con un CFArray que se devuelve desde CoreText. Tengo este codigo
let lines: CFArrayRef = CTFrameGetLines(frame)
Veo dos formas posibles de acceder a los miembros de esta matriz. Tampoco está funcionando para mí en este momento.
Manera # 1 - Usa el CFArray directamente
let line: CTLineRef = CFArrayGetValueAtIndex(lines, 0)
Esto produce el error "''ConstUnsafePointer <()>'' en no convertible a ''CTLineRef''". Casting no parece cambiar este error.
Del mismo modo, me encantaría usar líneas "indistintamente" como una matriz Swift como dice que puedo. Sin embargo,
let line: CTLineRef = lines[0]
produce el error "''CFArrayRef'' no tiene un miembro llamado ''subíndice''"
Manera # 2 - Convertir el CFArray a una matriz Swift
var linesArray: Array = [CTLineRef]()
linesArray = bridgeFromObjectiveC(lines, linesArray.dynamicType)
Aquí, declaré una matriz Swift y la puse igual a la CFArray puenteada. Esto se compila sin error, pero cuando lo ejecuto, obtengo un bloqueo EXC_BREAKPOINT en la segunda línea. Tal vez no estoy usando el lenguaje Swift correctamente en este ...
Al parecer, en Swift 2 puedes lanzar CFArray as [AnyObject]
.
Pasé mucho tiempo averiguando por qué mi código dejó de funcionar después de la conversión de Swift 1.2. En mi caso resultó que algunas API cambiaron de CFArray! para CFArray? y este elenco estaba volviendo a cero:
let cfArray = ... // Function that returns CFArray? instead of CFArray!
if let array = cfArray as? [AnyObject] { // nil
...
No hay ninguna advertencia de que, opcionalmente, estoy lanzando opcional a no opcional, lo que no tiene sentido.
Aquí se explica cómo hacerlo, según el estado actual del compilador Swift y la documentación de Swift. Esperemos que esto se limpie en betas posteriores.
ACTUALIZACIÓN: Desde la versión Beta 5, reinterpretCast ha cambiado su nombre a unsafeBitCast, y un objeto CTLine debe enviarse como entrada. La manera # 2 todavía no funciona.
Manera # 1 - Usa el CFArray directamente
let line: CTLine = reinterpretCast(CFArrayGetValueAtIndex(lines, 0))
Respecto a los comentarios de Gary Makin: la referencia puede ser eliminada de CTLineRef, pero esto no cambia ARC vs no ARC. De acuerdo con Uso de Swift con Cocoa y Objective-C, páginas 53-54, ref y non-ref son idénticos al compilador. Intentar llamar a CFRelease provoca un error del compilador.
Manera # 2 - Convertir el CFArray a una matriz Swift - Actualmente no funciona
Idealmente, queremos convertir las líneas en una matriz Swift de objetos CTLine, ya que sabemos que es lo que devuelve CTFrameGetLines, lo que nos brinda seguridad de tipos después de la conversión. Probablemente debido a un error del compilador, la matriz se puede convertir a una matriz [AnyObject], pero no a [CTLine]. Según la documentación de Apple , esto debería funcionar:
let linesNS: NSArray = CTFrameGetLines(frame)
let linesAO: [AnyObject] = linesNS as [AnyObject]
let lines: [CTLine] = linesAO as [CTLine]
Esto convierte CFArray en NSArray, luego NSArray en Swift Array [AnyObject], y luego reduce la matriz al tipo específico CTLine. Esto se compila, pero cuando se ejecuta, hay un bloqueo EXC_BREAKPOINT en la última línea.
Basado en la respuesta del purrrminador:
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)
}
En Swift 2.0, he hecho la conversión de CFArray a Array con el siguiente código:
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
}
}
Espero que haya una mejor manera.
Recién probado en 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 { /**/ }
No estoy seguro de si el reparto intermedio a NSArray es necesario, pero eso me servirá por ahora 😉
PD: también funciona con .takeRetainedValue
.