При использовании Swift предполагается, что фреймворки Cocoa возвращают родные типы Swift, даже несмотря на то, что фреймворки фактически возвращают объекты Objective-C. Аналогично, методы принимают типы Swift как параметры, где это имеет смысл.
Предположим, что я хочу вызвать метод Cocoa, который (в Objective-C) предоставит мне NSArray, а затем передаст это методу Cocoa, который примет значение NSArray
. С кодом, подобным этому:
let a: [AnyObject] = [] // Imagine calling a method that returns a huge NSArray.
let mutable = NSMutableArray()
mutable.addObjectsFromArray(a)
Похоже, что огромный NSArray
собирается подключиться к массиву Swift при назначении a
, а затем передается обратно в NSArray
при передаче в качестве параметра. По крайней мере, как это выглядит при профилировании и рассмотрении разборки.
Есть ли способ избежать этих потенциально медленных преобразований, когда мне не нужно действительно работать с массивом в Swift? Когда я просто получаю его от Cocoa, а затем передаю его обратно в Cocoa?
Сначала я подумал, что это поможет добавить информацию типа для a
:
let a: NSArray = [] // Imagine calling a method that returns a huge NSArray.
let mutable = NSMutableArray()
mutable.addObjectsFromArray(a as [AnyObject])
Но потом мне нужно преобразовать параметр в массив Swift позже или компилятор будет жаловаться.
Кроме того, разборка для кода типа:
let c: NSArray = mutable.subarrayWithRange(NSMakeRange(0, 50))
показывает вызовы __TF10Foundation22_convertNSArrayToArrayurFGSqCSo7NSArray_GSaq__
и __TFer10FoundationSa19_bridgeToObjectiveCurfGSaq__FT_CSo7NSArray
, по-видимому, преобразуя возвращаемое значение в Swift, а затем обратно в Objective-C. (Это происходит даже с версиями Release.) Я надеялся, что, набрав c
как NSArray
, не потребуется никакого моста.
Я обеспокоен тем, что это может привести к неэффективности с очень большими структурами данных со многими разрозненными преобразованиями обычных и с коллекциями, которые ленивы/прокси, потому что они не обязательно большие, но могут быть дорогими для вычисления. Было бы неплохо иметь возможность получать такой массив из кода Objective-C и передавать его обратно, не выполняя все элементы массив, если они никогда не доступны из Swift.
Это очень другое производительность чем с Core Foundation/Foundation, где мосты были бесплатными. Существует так много случаев, когда код передает объекты назад и вперед, предполагая, что это будет O (1), и если они невидимо изменены на O (n), внешние алгоритмы могут стать квадратичными или худшими. Мне непонятно, что делать в этом случае. Если нет способа отключить мост, кажется, что все, что касается этих объектов, должно быть переписано в Objective-C.
Вот пример временного кода на основе приведенного выше примера:
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)
}
}
}
В этом примере testSwift()
примерно на 22% медленнее, чем testObjC()
. Просто для удовольствия я попытался добавить массив с помощью собственного массива Swift, и это было намного медленнее.
Связанная проблема заключается в том, что когда код Objective-C передает код Swift a NSMutableString
, Swift String
заканчивается копией изменяемой строки. Это хорошо в том смысле, что он не будет неожиданно изменен позади Свифт. Но если все, что вам нужно сделать, это передать строку Swift и взглянуть на нее кратко, эта копия может добавить неожиданные накладные расходы.