有没有办法在没有桥接的情况下在Swift中使用Foundation对象(NSString,NSArray,NSDictionary)?

Mic*_*sai 18 macos cocoa swift

使用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)
Run Code Online (Sandbox Code Playgroud)

看起来巨大NSArray的东西将被分配到Swift数组a,然后NSArray在作为参数传递时桥接回一个Swift数组.至少这是从剖析和看反汇编看起来的样子.

当我不需要在Swift中实际使用数组时,有没有办法避免这些可能很慢的转换?当我刚从Cocoa接收它然后将它传回Cocoa时?

起初,我认为有助于添加类型信息a:

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

但是后来我必须将参数转换为Swift数组,否则编译器会抱怨.

此外,代码的反汇编如:

let c: NSArray = mutable.subarrayWithRange(NSMakeRange(0, 50))
Run Code Online (Sandbox Code Playgroud)

显示调用__TF10Foundation22_convertNSArrayToArrayurFGSqCSo7NSArray_GSaq____TFer10FoundationSa19_bridgeToObjectiveCurfGSaq__FT_CSo7NSArray,似乎将返回值转换为Swift,然后返回到Objective-C.(即使使用Release版本也会发生这种情况.)我希望通过键入来完成,c因为NSArray不需要桥接.

我担心这会导致非常大的数据结构效率低下,许多不同的常规转换,以及懒惰/代理的集合,因为它们不一定很大但计算起来可能很昂贵.能够从Objective-C代码接收这样的数组并将其传递回去而不必实现数组的所有元素(如果它们永远不会从Swift访问)那将是很好的.

这是一个非常不同的 性能模型,而不是核心基金会/基金会,其中桥接是免费的.有很多情况下,代码来回传递对象,假设它是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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,testSwift()比约慢22%testObjC().只是为了好玩,我尝试使用本机Swift数组进行数组追加,这速度要慢得多.

一个相关的问题是,当Objective-C代码传递Swift代码时NSMutableString,Swift String最终会得到一个可变字符串的副本.这是好的,因为它不会在Swift的背后突然变异.但是,如果你需要做的只是将一个字符串传递给Swift并简要地查看它,这个副本可能会增加意想不到的开销.

Kni*_*gon 1

你尝试过延期吗?

extension NSMutableArray
{
    func addObjectsFromNSArray(array:NSArray)
    {
         for item in array
         {
              self.addObject(item);
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我有时间实际玩这个而不是理论上谈论,我将修改我的答案

创建一个扩展,但在目标 c 文件中执行它

@interface NSMutableArray(Extension)
    - (void)addObjectsFromNSArray:(NSObject*) array;
@end

@implementation NSMutableArray(Extension)
    - (void)addObjectsFromNSArray:(NSObject*) array
{
   [self addObjectsFromArray:(NSArray*)array];
}
@end
Run Code Online (Sandbox Code Playgroud)

我发现这样做代码的运行速度要快得多。(根据我的测试,几乎是 2 倍)

测试Swift 4.06 秒

testSwiftPure 7.97 秒

testSwiftExtension 2.30 秒