Swift Copy-On-Write

一.堆棧

棧是一塊空間較小但是運(yùn)行速度很快的內(nèi)存區(qū)域张惹,棧上的內(nèi)存分配遵循后進(jìn)先出的原則疯兼,通過移動棧的尾指針實(shí)現(xiàn)push和pop操作教馆。
堆是內(nèi)存中的另外一塊馋缅,空間比棧大很多扒腕,但是運(yùn)行速度比棧要慢。但是堆可以動態(tài)分配內(nèi)存萤悴。堆的內(nèi)存分配比較復(fù)雜瘾腰,系統(tǒng)需要在堆上不斷尋找不再需要的內(nèi)存然后進(jìn)行回收。在ARC中上述過程是自動的覆履。另外在多線程環(huán)境中蹋盆,多個(gè)線程會共享堆內(nèi)存。為了確保線程安全硝全,堆會對資源進(jìn)行加鎖操作栖雾。但是加鎖是很耗費(fèi)性能的,你在堆上所獲得的數(shù)據(jù)安全性實(shí)際上是在犧牲性能的代價(jià)下得來的伟众。

二.swift中的值類型和引用類型

1.值類型

在Swift中析藕,值類型有兩種。一種是定長值類型凳厢,比如數(shù)值類型Int,Double,Float账胧,還有一些只包含定長值類型的結(jié)構(gòu)體(CGPoint)等等;另外一種叫做變長值類型先紫,比如String找爱,數(shù)組,字典等等泡孩。定長值類型都會保存在棧上车摄,而變長值類型則會分配堆內(nèi)存。
值類型的實(shí)例(結(jié)構(gòu)體)只會在棧上保存它內(nèi)部的存儲屬性,并且通過=賦值的實(shí)例彼此的存儲是獨(dú)立的吮播。也就是我們所說的拷貝变屁。如下:

struct Point{
  var x,y:Double
}
let point1 = Point(x:3,y:5)
var point2 = point1

point1和point2會被分配到棧上,并且會分別為point1和point2分配內(nèi)存空間意狠。在語句point2 = point1的時(shí)候粟关,point1進(jìn)行了拷貝。因?yàn)槎ㄩL值類型的空間是固定的环戈,所以這種拷貝的開銷很小闷板。

2.引用類型

引用類型并不會直接保存在棧上,還是以上述Point為例院塞,如果把Point修改為類遮晚,并生成兩個(gè)引用point1和point2,這時(shí)系統(tǒng)會在棧上開辟兩個(gè)指針長度來保存point1和ponit2指針拦止,棧上的指針負(fù)責(zé)去堆上找對應(yīng)的對象县遣。point1和point2所指向的實(shí)例的存儲屬性會保存在堆上。
在棧上生成point1指針后汹族,指針內(nèi)容是空的萧求,接下來會去堆上分配內(nèi)存,首先會對堆加鎖顶瞒,找到尺寸合適的空間夸政,然后分配目標(biāo)內(nèi)存并解除堆的鎖定,將堆內(nèi)存片段的首地址保存在棧的指針中榴徐。相比在棧上保存point1和point2的指針守问,堆上需要的空間更大。除了x和y的空間箕速,在頭部還有8個(gè)字節(jié)的空間酪碘,一個(gè)用來索引類的類型信息的指針地址,一個(gè)用來保存對象的引用計(jì)數(shù)盐茎。當(dāng)使用=賦值時(shí)兴垦,棧上會生成point2指針,point1和point2指針指向同一個(gè)堆地址
引用類型的賦值不會發(fā)生拷貝字柠。所以無論改變point1或者是point2的屬性探越,改變的都是同一塊堆地址上的屬性。這里要特別說明let和var窑业。swift提供了let和var來限制對象的可變性和不可變性钦幔,但是對于某個(gè)實(shí)例,有意義的是其內(nèi)部屬性常柄。如果你用let聲明一個(gè)引用類型對象鲤氢,你只能保證它的指針地址不能被改變搀擂,但是不能約束它的內(nèi)部屬性。舉個(gè)例子:

//這里的Point是Class
//聲明point1和point2指向不同的內(nèi)存地址
let point1 = Point(x:3,y:5)
let point2 = Point(x:5,y:1)
point1 = point2 //發(fā)生編譯錯(cuò)誤卷玉,不能修改point1的指針
point1.x = 0 //因?yàn)閤是用var定義的所以可以修改
 //這里point1.x == 0

我們把多個(gè)引用指向同一塊內(nèi)存稱為資源共享哨颂。不過在實(shí)際開發(fā)過程中,很多時(shí)候我們并不想讓point1和point2資源共享相种,這樣會造成很多難以判斷的錯(cuò)誤威恼。在Swift中,一種新的類型來專門解決共享的問題寝并,就是我們所說的變長值類型箫措。剛才講值類型的時(shí)候有提過定長值類型的內(nèi)存分配,那么變長值類型是什么樣的衬潦?這就是我們今天的主題Copy-On-Write斤蔓。

三.Copy-On-Write

因?yàn)闂I系目臻g是連續(xù)的,你總是通過移動棧尾指針去開辟和釋放棧內(nèi)存别渔,而變長值類型中有一些成員在初始化的時(shí)候并不能確定它所占用的內(nèi)存附迷。比如集合類型惧互,你可以隨時(shí)往里面添加和刪除元素哎媚,這會導(dǎo)致內(nèi)存的增加和減少。類似的還有字符串喊儡,在內(nèi)存中儲存字符串實(shí)際上是存儲的每一個(gè)字符拨与,所以對于變長值類型并不能把全部內(nèi)容都保存在棧上。在Swift中用了一種很巧妙的技術(shù)來實(shí)現(xiàn)變長值類型艾猜,那就是Copy-On-Write买喧。

Copy-On-Write故名思議就是寫時(shí)復(fù)制,當(dāng)我們對變量進(jìn)行寫操作的時(shí)候會觸發(fā)拷貝操作匆赃。但是我們也不能在每一次寫入的時(shí)候都拷貝淤毛,思考一下,如果該變量的引用計(jì)數(shù)只有1算柳,那就沒有任何拷貝的必要低淡。所以在拷貝前我們需要檢測變量的引用計(jì)數(shù)是否唯一。在swift中提供了isKnownUniquelyReferenced瞬项,它能檢查一個(gè)類的實(shí)例是不是唯一的引用蔗蹋。然而這個(gè)方法只能對Swift的類使用,所以對于不是Swift的類我們需要在外面包裝一下囱淋。下面我們看代碼:

import UIKit
//聲明swift包裝類猪杭,用于包裝OC對象UIBezierPath
class Box<T>{
    var rawValue:T
    init(rawValue:T) {
        self.rawValue = rawValue
    }
}
struct BezierPath{
    private var _path = Box.init(rawValue: UIBezierPath())
    var pathForReading:Box<UIBezierPath>{
        return _path
    }
    var pathForWriting:Box<UIBezierPath>{
        //mutating 聲明的方法可以修改結(jié)構(gòu)體中變量
        //isKnownUniquelyReferenced 檢測引用類型的引用是否唯一 但是只對swift類有用 這里我們針對的對象是UIBezierPath 所以我們需要用Swift的類包裝一下 在這里我們聲明了Box類
        mutating get{
            if !isKnownUniquelyReferenced(&_path){
                _path = Box.init(rawValue: _path.rawValue.copy() as! UIBezierPath)
                print("拷貝")
                return _path
            }
            print("未拷貝")
            return _path
        }
    }
}


class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        var bizer = BezierPath()
        bizer.pathForWriting.rawValue.lineWidth = 3
        //內(nèi)部Box對象引用計(jì)數(shù)為1,不拷貝
        bizer.pathForWriting.rawValue.lineWidth = 10

        //Box對象引用計(jì)數(shù)+1
        //bizer和bizer1會共享內(nèi)部Box對象
        //要注意這里的賦值把bizer進(jìn)行了拷貝妥衣,但是其內(nèi)部的引用屬性還是指向相同地址皂吮。
        var bizer1 = bizer
        
        bizer1.pathForWriting.rawValue.lineWidth = 5
        
        print(bizer.pathForReading.rawValue.lineWidth) //輸出10
        // Do any additional setup after loading the view, typically from a nib.
    }
}

相關(guān)的地方在代碼中都有給出注釋戒傻,所以在這里就不在贅述。

完蜂筹!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末稠鼻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狂票,更是在濱河造成了極大的恐慌候齿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闺属,死亡現(xiàn)場離奇詭異慌盯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掂器,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門亚皂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人国瓮,你說我怎么就攤上這事灭必。” “怎么了乃摹?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵禁漓,是天一觀的道長。 經(jīng)常有香客問我孵睬,道長播歼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任掰读,我火速辦了婚禮秘狞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蹈集。我一直安慰自己烁试,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布拢肆。 她就那樣靜靜地躺著减响,像睡著了一般。 火紅的嫁衣襯著肌膚如雪善榛。 梳的紋絲不亂的頭發(fā)上辩蛋,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音移盆,去河邊找鬼悼院。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咒循,可吹牛的內(nèi)容都是我干的据途。 我是一名探鬼主播绞愚,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颖医!你這毒婦竟也來了位衩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤熔萧,失蹤者是張志新(化名)和其女友劉穎糖驴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體佛致,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贮缕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俺榆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片感昼。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖罐脊,靈堂內(nèi)的尸體忽然破棺而出定嗓,到底是詐尸還是另有隱情,我是刑警寧澤萍桌,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布宵溅,位于F島的核電站,受9級特大地震影響梗夸,放射性物質(zhì)發(fā)生泄漏层玲。R本人自食惡果不足惜号醉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一反症、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧畔派,春花似錦铅碍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至憨愉,卻和暖如春烦绳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背配紫。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工径密, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人躺孝。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓享扔,卻偏偏與公主長得像底桂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子惧眠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容