關(guān)于 Swift 4 中內(nèi)存安全訪問

本文主要翻譯今年 The Swift Programming Language (Swift 4) 中新出的章節(jié) -《Memory Safety》。在 Swift 4 中,內(nèi)存安全訪問進(jìn)行很大的優(yōu)化《What's New in Swift 4 ?》。

默認(rèn)情況下,Swift 會克服代碼層面上的一些不安全的行為,如:確保一個(gè)變量被初始化完后才能被訪問、確保變量在銷毀后不會被訪問等等安全操作。

Swift 也會確保在?多路訪問內(nèi)存中同一區(qū)域時(shí)不會沖突(獨(dú)占訪問??該區(qū)域)。通常情況下,我們完全無需考慮內(nèi)存訪問?沖突的問題,因?yàn)?Swift 是自動(dòng)管理內(nèi)存的。然而,在?碼代碼的時(shí)候,了解那些地方可能發(fā)生內(nèi)存訪問沖突是非常重要的。通常情況下,如果你的代碼有?內(nèi)存訪問沖突,那么 Xcode 會提示編譯?錯(cuò)誤或者運(yùn)行時(shí)錯(cuò)誤。

本文不會介紹什么是內(nèi)存?訪問沖突。詳見 The Swift Programming Language (Swift 4)。如果你寫的是并發(fā)或者多線程的程序,內(nèi)存沖突訪問與單線程是非常相似的一個(gè)問題。本文主要討論?單線程上的內(nèi)存沖突訪問。如果想檢測多線程是否存在內(nèi)存訪問沖突,你可以看看這篇文檔。

我們可以把訪問分為兩種:即時(shí)和長期(instantaneous & long-term)

  • 即時(shí)訪問:即在訪問開始至?結(jié)束前都不可能有其他?代碼來?訪問同一區(qū)域。
  • 長期訪問:即在訪問開始至?結(jié)束前可能有其他代碼來訪問同一區(qū)域。??長期訪問可能和其他即時(shí)訪問或者長期?訪問重疊。

重疊訪問主要帶有 in-out 參數(shù)的函數(shù)(或方法)以及結(jié)構(gòu)體中帶有 mutating 關(guān)鍵字的方法。我們下面來看看?例子。

In-Out 參數(shù)的訪問沖突

?一個(gè)函數(shù)對其 in-out 參數(shù)具有長期的訪問權(quán)限,如下代碼:

Excerpt From: Apple Inc. "The Swift Programming Language (Swift 4).” iBooks".


var stepSize = 1
 
func increment(_ number: inout Int) {
    number += stepSize
}
 
increment(&stepSize)
// Error: conflicting accesses to stepSize

在上述代碼中,stepSize 是一個(gè)全局變量,而且被作為一個(gè) in-out 參數(shù)傳給 increment(_:) 方法。沖突的原因在于 numberstepSize 引用的是?內(nèi)存中同一區(qū)域,并且同時(shí)進(jìn)行讀寫訪問,因此導(dǎo)致訪問沖突。

memory_increment_2x

我們可以?采用復(fù)制 stepSize 的方式解決該問題:


// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)
 
// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2
// stepSize is now 2

?self 的訪問沖突

在結(jié)構(gòu)體中,帶有 mutating 關(guān)鍵字的方法調(diào)用期間對 self 具有寫入權(quán)限。


extension Player {
    mutating func shareHealth(with teammate: inout Player) {
        balance(&teammate.health, &health)
    }
}
 
var oscar = Player(name: "Oscar", health: 10, energy: 10)

var maria = Player(name: "Maria", health: 5, energy: 10)

oscar.shareHealth(with: &maria)  // OK

?上述代碼是 Ok 的,?即時(shí)寫入權(quán)限在時(shí)間上是重疊的,但是是分別訪問 oscar 的 health 和 maria 的 health,因此在 shareHealth(with:) 方法中并沒有發(fā)生內(nèi)存訪問沖突。

memory_share_health_maria_2x

然而,如果你把 oscar 作為參數(shù)傳給 shareHealth(with:),那么?就會產(chǎn)生內(nèi)存訪問沖突:


oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar

很顯然,shareHealth(with:) 方法中的 ?selfteammate 同時(shí)指向內(nèi)存中同一區(qū)域,即同時(shí)對 oscarhealth 進(jìn)行讀寫訪問,因此導(dǎo)致訪問沖突。

memory_share_health_oscar_2x

屬性的訪問沖突

像結(jié)構(gòu)體、?元組、枚舉這些類型都是由各個(gè)值組成的,如:結(jié)構(gòu)體的?各種屬性、元組的各種元素等等。因?yàn)?它們都是值類型,這意味著對其中一個(gè)屬性的讀寫訪問就是對整個(gè)值進(jìn)行讀寫訪問。代碼如下:


var playerInformation = (health: 10, energy: 20)

balance(&playerInformation.health, &playerInformation.energy)

// Error: conflicting access to properties of playerInformation

上述代碼不難理解,因?yàn)樵媸侵殿愋停鲜?balance(_:_:) 發(fā)生內(nèi)存訪問沖突,即同時(shí)訪問 playerInformation。

下面我們再看一下結(jié)構(gòu)體,其中 holly 是一個(gè)全局變量


var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy)  // Error

上述代碼會報(bào)這樣一個(gè)錯(cuò)誤:Simultaneous accesses to 0x10****580, but modification requires exclusive access。?其實(shí)就是內(nèi)存訪問沖突了,Swift 4 中也針對這塊做了優(yōu)化處理,?感興趣的同學(xué)可以?查閱我之前寫的一篇文章《[WWDC17] What's New in Swift 4 ?》。

在實(shí)踐中,上述代碼中的 holly 一般是個(gè)局部變量而非全局變量,編譯器可以保證對結(jié)構(gòu)體的存儲屬性?進(jìn)行重疊訪問是安全的,代碼如下:


func someFunction() {
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    balance(&oscar.health, &oscar.energy)  // OK
}

上述代碼?運(yùn)行是 Ok 的,有時(shí)候,限制?結(jié)構(gòu)體的各屬性進(jìn)行重疊訪問是沒有必要的,這也就是為什么 someFunction() 沒有發(fā)生沖突訪問的原因。內(nèi)存訪問安全雖應(yīng)當(dāng)?shù)玫奖WC,但是獨(dú)占訪問比內(nèi)存?安全訪問要求更加嚴(yán)格,從上述代碼可看出,即時(shí)違背了獨(dú)占訪問的原則,內(nèi)存安全也能得到保證。一般情況下,編譯器會在如下條件下保證對結(jié)構(gòu)體的存儲屬性?進(jìn)行安全的重疊訪問:

  • 只訪問某個(gè)實(shí)例的存儲屬性,而不是計(jì)算屬性或類屬性
  • 訪問的是?局部的結(jié)構(gòu)體變量,而不是全局變量
  • 結(jié)構(gòu)體沒有被任何閉包所捕獲,或者僅被非逃逸閉包捕獲。

感興趣的同學(xué)可以查閱這里 The Swift Programming Language (Swift 4)。

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

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,694評論 4 61
  • 你不懂我的悲傷 像觸及不到的高墻 我翹首盼望,你遙遙無期 一層塵霜落滿了心房。 你不懂我的悲傷 像滴答鐘聲走走停停...
    sunshine小坐閱讀 418評論 0 1
  • 張守艷 1、起居規(guī)矩 夏季則宜晚睡早起,中午盡可能午睡。切記不能在樓道、屋檐下或通風(fēng)口的陰涼處久坐、久臥、久睡。更...
    xiaoyanzi888閱讀 317評論 0 0
  • 今天六點(diǎn)半起床,上午去武大辦事,下午去學(xué)校做活動(dòng),這幾天一直在思考該怎樣把達(dá)美前臺這份工作做好,我計(jì)劃花時(shí)間把需要...
    星鑠閱讀 198評論 1 1
  • ???你認(rèn)為的寺院生活是什么樣的? 酒肉穿腸過,佛祖心中留? 8小時(shí)工作制,私生活自由? ??No!No!No! ...
    蚍蜉觀察閱讀 373評論 0 1

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