淺談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。

參考

相關文章
相關標籤/搜索