《Advanced Swift》筆記:在Swift結(jié)構(gòu)體中實(shí)現(xiàn)寫(xiě)時(shí)復(fù)制

原文地址:在Swift結(jié)構(gòu)體中實(shí)現(xiàn)寫(xiě)時(shí)復(fù)制

結(jié)構(gòu)體(Struct)在Swift中占有重要地位,在Swift標(biāo)準(zhǔn)庫(kù)中,大約有90%的公開(kāi)類型都是結(jié)構(gòu)體,包括我們常用的Array、String、Dictionary。結(jié)構(gòu)體相比類,一個(gè)最重要的特性就是它是值類型,而類似引用類型。值類型是通過(guò)復(fù)制值來(lái)賦值的,而不是引用同一個(gè)內(nèi)存地址,這樣就不存在數(shù)據(jù)共享的問(wèn)題,能防止意外的數(shù)據(jù)改變,并且它是線程安全的。

舉一個(gè)很簡(jiǎn)單的例子,在objc中,數(shù)組是類,是引用類型,在Swift中,數(shù)組是結(jié)構(gòu)體,是值類型。因此下面的代碼中:


let array1 = NSMutableArray(array: ["lihua", "liming"])
let array2 = array1

array1.addObject("xiaowang")
array2

array1array2最后都變成了["lihua", "liming", "xiaowang"],也就是array1的改變會(huì)導(dǎo)致array2也發(fā)生改變,因?yàn)樗鼈儍蓚€(gè)都是引用類型,并且都引用了同一個(gè)內(nèi)存地址。

而在Swift中,就不存在這樣的問(wèn)題:


var array3 = ["lihua", "liming"]
var array4 = array3

array3.append("xiaowang")
array4

這段代碼執(zhí)行后,array3變成了["lihua", "liming", "xiaowang"],而array4還是["lihua", "liming"]。這就是結(jié)構(gòu)體和類的最大區(qū)別。

那么,是不是每次將struct賦值給其它變量或者傳遞給函數(shù)時(shí)都會(huì)發(fā)生復(fù)制呢。答案是否定的,在Swift中的Array、Dictionary、String這些類型中,盡管它們都是值類型,但在Swift的具體實(shí)現(xiàn)中做了優(yōu)化,可以避免不必要的復(fù)制。在《The Swift Programming Language (Swift 2.2)》一書(shū)的“Classes and Structures”一章末尾寫(xiě)道:

The description above refers to the “copying” of strings, arrays, and dictionaries. The behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.

在Swift中采用的優(yōu)化方式叫做寫(xiě)時(shí)復(fù)制技術(shù),簡(jiǎn)單的說(shuō)就是,只有當(dāng)一個(gè)結(jié)構(gòu)體發(fā)生了寫(xiě)入行為時(shí)才會(huì)有復(fù)制行為。具體的做法就是,在結(jié)構(gòu)體內(nèi)部用一個(gè)引用類型來(lái)存儲(chǔ)實(shí)際的數(shù)據(jù),在不進(jìn)行寫(xiě)入操作的普通傳遞過(guò)程中,都是將內(nèi)部的reference的應(yīng)用計(jì)數(shù)+1,在進(jìn)行寫(xiě)入操作時(shí),對(duì)內(nèi)部的reference做一次copy操作用來(lái)存儲(chǔ)新的數(shù)據(jù),防止和之前的reference產(chǎn)生意外的數(shù)據(jù)共享。

在Swift中有一個(gè)方法:isUniquelyReferencedNonObjC(Swift 2.2),在Swift3中這個(gè)函數(shù)變成了這樣:isKnownUniquelyReferenced,他能檢查一個(gè)類的實(shí)例是不是唯一的引用,如果是,我們就不需要對(duì)結(jié)構(gòu)體實(shí)例進(jìn)行復(fù)制,如果不是,說(shuō)明對(duì)象被不同的結(jié)構(gòu)體共享,這時(shí)對(duì)它進(jìn)行更改就需要進(jìn)行復(fù)制。

但這個(gè)函數(shù)只對(duì)Swift對(duì)象有用,如果要用在Objective-C對(duì)象上,可以將OC對(duì)象用Swift進(jìn)行一次封裝。

下面是《Advanced Swift》書(shū)中的一個(gè)實(shí)現(xiàn)寫(xiě)時(shí)復(fù)制技術(shù)的代碼實(shí)例,我已經(jīng)把它轉(zhuǎn)為Swift3了:


final class Box<A> {
  var unbox: A
  init(_ value: A) {
    unbox = value
  }
}

struct GaussianBlur {
  private var boxedFilter: Box<CIFilter> = {
    var filter = CIFilter(name: "CIGaussianBlur", withInputParameters: [:])!
    filter.setDefaults()
    return Box(filter)
  }()

  fileprivate var filter: CIFilter {
    get { return boxedFilter.unbox }
    set { boxedFilter = Box(newValue) }
  }

  private var filterForWriting: CIFilter {
    mutating get {
      if !isKnownUniquelyReferenced(&boxedFilter) {
        filter = filter.copy() as! CIFilter
      }
      return filter
    }
  }

  var inputImage: CIImage {
    get { return filter.value(forKey: kCIInputImageKey) as! CIImage }
    set { filterForWriting.setValue(newValue, forKey: kCIInputImageKey) }
  }
  var radius: Double {
    get { return filter.value(forKey: kCIInputRadiusKey) as! Double }
    set { filterForWriting.setValue(newValue, forKey: kCIInputRadiusKey) }
  }
}

extension GaussianBlur {
  var outputImage: CIImage? {
    return filter.outputImage
  }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容