浅谈Swift中的Copy-on-Write

开篇

在Swift中,有两种传值方式:引用类型(Class)和值类型(Struct/Enum)。而值类型有一个copy的操做,它的意思是当你传递一个值类型的变量的时候(给一个变量赋值,或者函数中的参数传值),它会拷贝一份新的值让你进行传递。你会获得拥有相同内容的两个变量,分别指向两块内存。git

这样的话,在你频繁操做占用内存比较大的变量的时候就会带来严重的性能问题,Swift也意识到了这个问题,因此推出了Copy-on-Write机制,用来提高性能。下面咱们来讲一下什么是Copy-on-Write。github

什么是Copy-on-Write

当你有一个占用内存很大的一个值类型,而且不得不将它赋值给另外一个变量或者当作函数的参数传递的时候,拷贝它的值是一个很是消耗内存的操做,由于你不得不拷贝它全部的东西放置在另外一块内存中。swift

为了优化这个问题,Swift对于一些特定的值类型(集合类型:Array、Dictionary、Set)作了一些优化,在对于Array进行拷贝的时候,当传递的值进行改变的时候才会发生真正的拷贝。而对于String、Int等值类型,在赋值的时候就会发生拷贝。下面来看代码验证一下:bash

先看一下基本类型(Int、String等)

var num1 = 10
var num2 = num1
print(address(of: &num1)) //0x7ffee0c29828
print(address(of: &num2)) //0x7ffee0c29820

var str1 = "abc"          
var str2 = str1
print(address(of: &str1)) //0x7ffee0c29810
print(address(of: &str2)) //0x7ffee0c29800

//打印内存地址
func address(of object: UnsafeRawPointer) -> String {
    let addr = Int(bitPattern: object)
    return String(format: "%p", addr)
}
复制代码

从上面的打印咱们能够看出基本类型在进行赋值的时候就发生了拷贝操做app

在看一下集合类型

var arr1 = [1,2,3,4,5]
var arr2 = arr1
print(address(of: &arr1)) //0x6000023b06b0
print(address(of: &arr2)) //0x6000023b06b0

arr2[2] = 4
print(address(of: &arr1)) //0x6000023b06b0
print(address(of: &arr2)) //0x6000023b11f0
复制代码

从上面代码咱们能够看出,当arr1赋值给arr2时并无发生拷贝操做,当arr2的值改变的时候才发生了拷贝操做函数

Copy-on-Write是一种很是好的方式去优化值类型的拷贝,虽然对于这套底层逻辑咱们不用实现,可是了解这个机制对于咱们来讲仍是很是必要的。经过上面的代码咱们看到了Copy-on-Write机制是如何发生做用的,可是知道它如何应用是不够的,咱们要作到知其然而且知其因此然。因此,接下来咱们看一下Swift源代码是如何实现这一机制的。性能

Copy-on-Write如何实现的

你能够在OptimizationTips.rst里发现以下代码:优化

final class Ref<T> {
  var val : T
  init(_ v : T) {val = v}
}

struct Box<T> {
    var ref : Ref<T>
    init(_ x : T) { ref = Ref(x) }

    var value: T {
        get { return ref.val }
        set {
          if (!isUniquelyReferencedNonObjC(&ref)) {
            ref = Ref(newValue)
            return
          }
          ref.val = newValue
        }
    }
}
复制代码

该例子显示了如何用一个引用类型去实现一个拥有copy-on-write特性的泛型值类型T。具体逻辑就是当你进行set的时候判断是否有多个reference,若是是多个reference则进行拷贝,反之则不会。ui

总结

  • Copy-on-Write是一种用来优化占用内存大的值类型的拷贝操做的机制。
  • 对于Int、String等基本类型的值类型,它们在赋值的时候就会发生拷贝,它们没有Copy-on-Write这一特性(由于Copy-on-Write带来的开销每每比直接复制的开销要大)。
  • 对于Array、Dictionary、Set类型,当它们赋值的时候不会发生拷贝,只有在修改的以后才会发生拷贝,即Copy-on-Write。

参考

相关文章
相关标签/搜索