美文网首页
2.2 Swift 3 理解Array和NSArray的差异

2.2 Swift 3 理解Array和NSArray的差异

作者: CDLOG | 来源:发表于2019-01-15 10:55 被阅读0次

    同样是数组类型,Swift中的Array和Foundation中的NSArray有着截然不同的语义和用法。在这一节中,我们就来简单了解下这些区别。

    按值语义实现的Array

    在Swift中,Array是按照值语义实现的,当我们复制一个Array对象时,会拷贝整个Array的内容:

    var a = [1, 2, 3] // [1, 2, 3]
    let copyA = a     // [1, 2, 3]
    
    a.append(4)
    // a  [1, 2, 3, 4]
    // copyA [1, 2, 3]
    // copyA.append(4)   // Compile error
    
    

    上面的代码中,有两点值得说明。

    首先,Swift数组是否可以被修改完全是通过varlet关键字来决定的,Array类型自身并不解决它是否可以被修改的问题;

    其次,从结果可以看到,复制a并向a添加内容之后,copyA的内容并不会修改。但是,Swift在复制Array时,同样对Array的性能有所考量,它使用了copy on write的方式。即如果你仅仅复制了Array而不对它修改时,真正的复制是不会发生的,两个数组仍旧引用同一个内存地址。只有当你修改了其中一个Array的内容时,才会真正让两个Array对象分开。为了看到这个过程,我们先来实现一个方法,把保存Array内容的地址变成一个字符串:

    func getBufferAddress<T>(of array: [T]) -> String {
        return array.withUnsafeBufferPointer { buffer in
            return String(describing: buffer.baseAddress)
        }
    }
    
    

    其中,withUnsafeBufferPointerArray的一个方法,它可以把保存Array内容的地址,传递给它的closure参数。在我们的例子里,这个closure只是把Array的地址,变成了一个String对象。

    然后,我们在a.append(4)前后,分别观察acopyA的内容:

    getBufferAddress(of: a)
    getBufferAddress(of: copyA)
    
    a.append(4)
    
    getBufferAddress(of: a)
    getBufferAddress(of: copyA)
    
    
    Array vs nsarray

    就如同图中显示的,只有在给a添加内容后,它才被重新分配了内存地址。

    了解了Swift Array之后,我们再来看Foundation中NSArray的情况。

    按引用语义实现的NSArray

    在Foundation中,数组这个类型有两点和Swift Array是不同的:

    • 数组是否可以被修改是通过NSArrayNSMutableArray这两个类型来决定的;
    • NSArrayNSMutableArray都是类对象,复制它们执行的是引用语义;

    当把这两个因素放在一起的时候,Foundation中的“常量数组”这个概念就会在一些场景里表现的很奇怪。因为你还可以通过对一个常量数组的非常量引用去修改它,来看下面的例子:

    // Mutable array [1, 2, 3]
    let b = NSMutableArray(array: [1, 2, 3])
    // Const array [1, 2, 3]
    let copyB: NSArray = b
    
    // [0, 1, 2, 3]
    b.insert(0, at: 0)
    // [0, 1, 2, 3]
    copyB
    
    

    从上面的代码可以看到,尽管我们在创建copyB时,使用了NSArray,表明我们不希望它的值被修改,由于这个赋值执行的是引用拷贝,因此,实际上它和b指向的是同一块内存空间。因此,当我们修改b的内容时,copyB也就间接受到了影响。

    为了在拷贝NSArray对象时,执行值语义,我们必须使用它的copy方法复制所有的元素:

    let b = NSMutableArray(array: [1, 2, 3])
    let copyB: NSArray = b
    let deepCopyB = b.copy() as! NSArray
    
    b.insert(0, at: 0) // [0, 1, 2, 3]
    copyB              // [0, 1, 2, 3]
    deepCopyB          // [1, 2, 3]
    
    

    从注释中的结果,你就能很容易理解deep copy的含义了。

    当我们使用NSArrayNSMutableArray时,Swift中的varlet关键字就和数组是否可以被修改没关系了。它们只控制对应的变量是否可以被赋值成新的NSArrayNSMutableArray对象。

    What's next?

    了解了ArrayNSArray的差别之后,下一节中,我们结合一些常用的场景,来看一下Swift是如何在API的设计层面,向我们推荐Array的各种更合理用法的。

    相关文章

      网友评论

          本文标题:2.2 Swift 3 理解Array和NSArray的差异

          本文链接:https://www.haomeiwen.com/subject/cvqlkqtx.html