objective arreglos array macos swift cocoa

macos - arreglos - swift add element to array



¿Hay alguna forma de trabajar con objetos de Foundation(NSString, NSArray, NSDictionary) en Swift sin puente? (1)

¿Has intentado hacer una extensión?

extension NSMutableArray { func addObjectsFromNSArray(array:NSArray) { for item in array { self.addObject(item); } } }

Ahora que tuve tiempo de jugar con esto en lugar de hablar en teoría, voy a revisar mi respuesta

Cree una extensión, pero en su lugar, hágalo en un archivo objetivo c

@interface NSMutableArray(Extension) - (void)addObjectsFromNSArray:(NSObject*) array; @end @implementation NSMutableArray(Extension) - (void)addObjectsFromNSArray:(NSObject*) array { [self addObjectsFromArray:(NSArray*)array]; } @end

Encontré el código para trabajar mucho más rápido haciéndolo de esta manera. (Casi 2x de mis pruebas)

testSwift 4.06 segundos

testSwiftPure 7.97 segundos

testSwiftExtension 2.30 segundos

Cuando se utiliza Swift, se declara que los marcos de Cocoa devuelven tipos Swift nativos, incluso aunque los marcos realmente devuelvan objetos Objective-C. Del mismo modo, los métodos toman los tipos Swift como parámetros, donde eso tiene sentido.

Supongamos que quiero llamar a un método Cocoa que (en Objective-C) me daría un NSArray y luego pasaría eso a un método Cocoa que toma un NSArray . Con código como este:

let a: [AnyObject] = [] // Imagine calling a method that returns a huge NSArray. let mutable = NSMutableArray() mutable.addObjectsFromArray(a)

Parece que la enorme NSArray se conectará a una matriz Swift cuando se asigne a y luego se conectará nuevamente a una NSArray cuando se pase como parámetro. Al menos así es como se ve al perfilar y mirar el desmontaje.

¿Hay alguna manera de evitar estas conversiones potencialmente lentas cuando no necesito trabajar realmente con la matriz en Swift? ¿Cuándo lo recibo de Cocoa y luego se lo devuelvo a Cocoa?

Al principio, pensé que sería útil agregar información de tipo para:

let a: NSArray = [] // Imagine calling a method that returns a huge NSArray. let mutable = NSMutableArray() mutable.addObjectsFromArray(a as [AnyObject])

Pero luego tengo que convertir el parámetro a una matriz Swift más tarde o el compilador se quejará.

Además, el desmontaje de código como:

let c: NSArray = mutable.subarrayWithRange(NSMakeRange(0, 50))

muestra las llamadas a __TF10Foundation22_convertNSArrayToArrayurFGSqCSo7NSArray_GSaq__ y __TFer10FoundationSa19_bridgeToObjectiveCurfGSaq__FT_CSo7NSArray , aparentemente convirtiendo el valor de retorno en Swift y luego en un objeto. (Esto ocurre incluso con las versiones de lanzamiento). Tenía la esperanza de que al escribir c como NSArray no hubiera ningún puente necesario.

Me preocupa que esto pueda llevar a inefficiencies con estructuras de datos muy grandes, con muchas conversiones dispares de las regulares y con colecciones perezosas / proxiadas porque no son necesariamente grandes pero pueden ser costosas de calcular. Sería bueno poder receive una matriz de este tipo desde el código de Objective-C y devolverla sin tener que realizar todos los elementos de la matriz si nunca se accede a ellos desde Swift.

Este es un modelo de performance muy different de Core Foundation / Foundation, donde el puente fue gratuito. Hay tantos casos en los que el código pasa objetos de un lado a otro, suponiendo que será O (1), y si estos se cambian de forma invisible a O (n), los algoritmos externos podrían volverse cuadráticos o incluso peores. No me queda claro qué se supone que uno debe hacer en este caso. Si no hay forma de desactivar el puente, parece que todo lo que toca esos objetos debería ser reescrito en Objective-C.

Aquí hay un ejemplo de código de tiempo basado en el ejemplo anterior:

NSArray *getArray() { static NSMutableArray *result; if (!result) { NSMutableArray *array = [NSMutableArray array]; for (NSUInteger i = 0; i < 1000000; i++) { [array addObjectsFromArray:@[@1, @2, @3, @"foo", @"bar", @"baz"]]; } result = array; } return result; } @interface ObjCTests : XCTestCase @end @implementation ObjCTests - (void)testObjC { // 0.27 seconds [self measureBlock:^{ NSArray *a = getArray(); NSMutableArray *m = [NSMutableArray array]; [m addObjectsFromArray:a]; }]; } @end class SwiftTests: XCTestCase { func testSwift() { // 0.33 seconds self.measureBlock() { let a: NSArray = getArray() as NSArray let m = NSMutableArray() m.addObjectsFromArray(a as [AnyObject]) } } func testSwiftPure() { // 0.83 seconds self.measureBlock() { let a = getArray() var m = [AnyObject]() m.appendContentsOf(a) } } }

En este ejemplo, testSwift() es aproximadamente un 22% más lento que testObjC() . Solo por diversión, traté de hacer la matriz agregada con la matriz Swift nativa, y esto fue mucho más lento.

Un problema relacionado es que cuando el código de Objective-C pasa un código Swift a NSMutableString , Swift String termina con una copia de la cadena mutable. Esto es bueno en el sentido de que no será mutado inesperadamente a espaldas de Swift. Pero si todo lo que necesita hacer es pasar una cadena a Swift y verlo brevemente, esta copia podría agregar una sobrecarga inesperada.