Quando si utilizza Swift, i framework Cocoa vengono dichiarati per restituire i tipi nativi di Swift, anche se i framework restituiscono effettivamente oggetti Objective-C. Allo stesso modo, i metodi usano i tipi Swift come parametri, dove ciò ha senso.C'è un modo per lavorare con gli oggetti Foundation (NSString, NSArray, NSDictionary) in Swift senza eseguire il bridging?
Supponiamo di voler chiamare un metodo Cocoa che (in Objective-C) mi restituisce un NSArray e poi lo passa a un metodo Cocoa che prende uno NSArray
. Con il codice come questo:
let a: [AnyObject] = [] // Imagine calling a method that returns a huge NSArray.
let mutable = NSMutableArray()
mutable.addObjectsFromArray(a)
Sembra l'enorme NSArray
sta per arrivare a ponte a un array di Swift quando viene assegnato a a
e poi colmato di nuovo ad un NSArray
quando passato come parametro. Almeno è così che sembra dal profiling e guardando allo smontaggio.
C'è un modo per evitare queste conversioni potenzialmente lente quando non ho bisogno di lavorare effettivamente con la matrice in Swift? Quando lo ricevo da Cocoa e poi lo rimando a Cocoa?
In un primo momento, ho pensato che sarebbe utile per aggiungere informazioni sul tipo per a
:
let a: NSArray = [] // Imagine calling a method that returns a huge NSArray.
let mutable = NSMutableArray()
mutable.addObjectsFromArray(a as [AnyObject])
Ma poi devo convertire il parametro a un array Swift tardive e il compilatore si lamenta.
Inoltre, lo smontaggio per il codice del tipo:
let c: NSArray = mutable.subarrayWithRange(NSMakeRange(0, 50))
mostra le chiamate verso __TF10Foundation22_convertNSArrayToArrayurFGSqCSo7NSArray_GSaq__
e __TFer10FoundationSa19_bridgeToObjectiveCurfGSaq__FT_CSo7NSArray
, apparentemente la conversione del valore di ritorno a Swift e poi di nuovo a Objective-C. (Questo accade anche con le versioni di Release.) Speravo che digitando c
come NSArray
non ci fosse il necessario ponte.
Sono preoccupato che ciò possa portare a inefficiencies con strutture di dati molto grandi, con molte conversioni disparate di quelle regolari e con raccolte pigre/proxy perché non sono necessariamente grandi ma possono essere costose da calcolare. Sarebbe bello poter usare receive tale array dal codice Objective-C e passarlo indietro senza dover realizzare tutti gli elementi dell'array se non sono mai accessibili da Swift.
Questo è un modello molto differentperformance rispetto a Core Foundation/Foundation in cui il bridging era gratuito. Ci sono così tanti casi in cui il codice passa gli oggetti avanti e indietro supponendo che sarà O (1), e se questi sono modificati in modo invisibile in O (n) gli algoritmi esterni potrebbero diventare quadratici o peggiori. Non mi è chiaro cosa si dovrebbe fare in questo caso. Se non c'è modo di disattivare il bridging, sembra che tutto ciò che tocca quegli oggetti debba essere riscritto in Objective-C.
Ecco alcuni codice di temporizzazione campione in base all'esempio precedente:
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)
}
}
}
In questo esempio, testSwift()
è circa il 22% più lento rispetto testObjC()
. Solo per divertimento, ho provato a fare l'array append con l'array nativo di Swift, e questo è stato molto più lento.
Un problema correlato è che quando il codice Objective-C passa codice Swift un NSMutableString
, la Swift String
finisce con un copia della stringa mutevole.Questo è buono nel senso che non sarà inaspettatamente mutato dietro la schiena di Swift. Ma se tutto quello che devi fare è passare una stringa a Swift e guardarla brevemente, questa copia potrebbe aggiungere un sovraccarico inaspettato.
Avete fatto i test di sincronizzazione? Se no, stai solo indovinando. Non indovinare, misura. Anche perché stai creando un 'NSMutableArray' e assegnandolo a una variabile' let' che è immutabile "' let mutable = NSMutableArray() '. – zaph
@zaph Sì, in un test di temporizzazione che ho fatto, il 25% del tempo sembrava essere spesi avanti e indietro in questo caso significa che non assegnerò un altro oggetto a quella variabile.L'oggetto stesso può ancora essere mutevole, proprio come se fosse, per esempio, un 'NSMutableURLRequest' che –
Puoi aggiungere del codice per mostrare la conversione avanti e indietro? FYI ... puoi inizializzare un 'NSMutableArray' da un' NSArray': 'let mutable = NSMutableArray (array: a)' –