Swift 訪問沖突(in-out)

簡(jiǎn)介

默認(rèn)情況下,Swift可以防止代碼中發(fā)生不安全的行為.例如截驮,Swift確保變量在使用之前進(jìn)行初始化亏狰,在取消分配后不訪問內(nèi)存甩恼,并檢查數(shù)組索引是否存在越界錯(cuò)誤定枷。
Swift還確保對(duì)同一內(nèi)存區(qū)域的多次訪問不會(huì)發(fā)生沖突孤澎,因?yàn)樾枰薷膬?nèi)存中某個(gè)位置的代碼才能對(duì)該內(nèi)存進(jìn)行獨(dú)占訪問。因?yàn)?strong>Swift自動(dòng)管理內(nèi)存欠窒,所以大多數(shù)時(shí)候你根本不需要考慮訪問內(nèi)存覆旭。但是,了解潛在沖突可能發(fā)生的位置非常重要,這樣我們就可以避免編寫對(duì)內(nèi)存具有沖突訪問權(quán)限的代碼姐扮。如果你的代碼確實(shí)包含沖突絮供,那么你將收到編譯時(shí)或運(yùn)行時(shí)錯(cuò)誤。

內(nèi)存訪問沖突

當(dāng)我們?cè)O(shè)置變量的值或?qū)?shù)傳遞給函數(shù)的操作時(shí),會(huì)在代碼中訪問內(nèi)存.

// A write access to the memory where one is stored.
var one = 1

// A read access from the memory where one is stored.
print("We're number \(one)!")

當(dāng)代碼的不同部分試圖同時(shí)訪問內(nèi)存中的相同位置時(shí)茶敏,可能會(huì)發(fā)生沖突的內(nèi)存訪問。同時(shí)多次訪問內(nèi)存中的某個(gè)位置會(huì)產(chǎn)生不可預(yù)測(cè)或不一致的行為.在Swift中缚俏,有一些方法可以修改跨越多行代碼的值惊搏,從而可以嘗試在自己修改的過程中訪問一個(gè)值。

如果您編寫了并發(fā)或多線程代碼忧换,則對(duì)內(nèi)存的沖突訪問可能是一個(gè)熟悉的問題恬惯。但是,此處討論的沖突訪問可能發(fā)生在單個(gè)線程上亚茬,并且不涉及并發(fā)或多線程代碼酪耳。

特征

-至少一個(gè)寫操作
-訪問內(nèi)存中的相同位置
-他們的持續(xù)時(shí)間重疊

讀寫訪問之間的區(qū)別:寫訪問權(quán)限會(huì)更改內(nèi)存中的位置,但讀訪問權(quán)限則不會(huì).內(nèi)存中的位置指的是被訪問的內(nèi)容.例如刹缝,變量碗暗,常量或?qū)傩浴4鎯?chǔ)器訪問的持續(xù)時(shí)間是瞬時(shí)的或長(zhǎng)期的梢夯。

如果在訪問開始之后但在結(jié)束之前其他代碼無法運(yùn)行言疗,則訪問是即時(shí)
函數(shù)具有對(duì)其所有輸入輸出參數(shù)的長(zhǎng)期寫訪問權(quán)。

例如颂砸,下面代碼清單中的所有讀寫訪問都是即時(shí)的

func oneMore(than number: Int) -> Int {
    return number + 1
}

var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber)
// Prints "2"

但是噪奄,有幾種方法可以訪問內(nèi)存,稱為長(zhǎng)期訪問人乓,跨越其他代碼的執(zhí)行.即時(shí)訪問和長(zhǎng)期訪問之間的區(qū)別在于勤篮,其他代碼可以在長(zhǎng)期訪問開始之后但在結(jié)束之前運(yùn)行,這稱為重疊色罚。長(zhǎng)期訪問可以與其他長(zhǎng)期訪問和即時(shí)訪問重疊碰缔。

重疊訪問主要出現(xiàn)在使用函數(shù)和方法中的in-out修飾的參數(shù)或mutating修飾的方法代碼中。使用長(zhǎng)期訪問的特定Swift代碼類型將在下面的部分中討論保屯。

in-out

in-out是修飾函數(shù)參數(shù)類型,表示該參數(shù)在函數(shù)內(nèi)修改后(即函數(shù)返回后),其值為修改后的值.

1,適用類型為變量
2,in-out修飾后的參數(shù),在傳參時(shí)需&修飾

in-out 訪問沖突

var stepSize = 1

func increment(_ number: inout Int) {
   number += stepSize
}

increment(&stepSize)
// Error: conflicting accesses to stepSize
in-out
解決
// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)

// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2

方法內(nèi)訪問沖突

struct Player {
   var name: String
   var health: Int
   var energy: Int

   static let maxHealth = 10
   mutating func restoreHealth() {
       health = Player.maxHealth
   }
}

在上面的 restoreHealth()方法中,對(duì) self的寫訪問權(quán)限從方法的開頭開始手负,一直持續(xù)到方法返回為止.在這種情況下, restoreHealth()中沒有其他代碼可以重疊訪問Player實(shí)例的屬性姑尺。下面的 shareHealth(with :)方法將另一個(gè) Player實(shí)例作為 in-out參數(shù)竟终,從而創(chuàng)造了重疊訪問的可能性。

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

在上面的例子中切蟋,為Oscar的玩家調(diào)用shareHealth(with :)方法與Maria的玩家分享健康狀況不會(huì)引起沖突.在方法調(diào)用期間有一個(gè)對(duì)oscar的寫訪問權(quán)统捶,因?yàn)閛scar是變異方法中self的值,并且在相同的持續(xù)時(shí)間內(nèi)對(duì)maria進(jìn)行寫訪問,因?yàn)閙aria作為in-out參數(shù)傳遞喘鸟。

屏幕快照 2019-06-26 下午5.56.48.png

但是匆绣,如果您將oscar作為參數(shù)傳遞給shareHealth(with:),則存在沖突:

oscar.shareHealth(with: &oscar)
// Error: conflicting accesses to oscar
屏幕快照 2019-06-26 下午6.19.09.png

屬性訪問沖突

結(jié)構(gòu)什黑,元組和枚舉等類型由單個(gè)組成值組成崎淳,例如結(jié)構(gòu)的屬性或元組的元素.因?yàn)檫@些是值類型,所以改變值的任何部分都會(huì)改變整個(gè)值愕把,這意味著對(duì)其中一個(gè)屬性的讀或?qū)懺L問需要對(duì)整個(gè)值的讀或?qū)懺L問權(quán)限
例如拣凹,重疊對(duì)元組元素的寫訪問會(huì)產(chǎn)生沖突:

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

實(shí)際上,大多數(shù)對(duì)結(jié)構(gòu)屬性的訪問都可以安全地重疊恨豁。
例如嚣镜,如果上例中的變量holly更改為局部變量而不是全局變量,則編譯器可以證明對(duì)結(jié)構(gòu)的存儲(chǔ)屬性的重疊訪問是安全的:

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

在上面的例子中橘蜜,Oscar的health和energy 作為balance( :)輸入?yún)?shù)菊匿。編譯器可以證明保留了內(nèi)存安全性,因?yàn)閮蓚€(gè)存儲(chǔ)的屬性不會(huì)以任何方式進(jìn)行交互计福。
為了保持存儲(chǔ)器安全性跌捆,并不總是必須限制對(duì)結(jié)構(gòu)屬性的重疊訪問.內(nèi)存安全是理想的保證,但獨(dú)占訪問是比內(nèi)存安全更嚴(yán)格的要求 - 這意味著一些代碼可以保持內(nèi)存安全棒搜,即使它違反了對(duì)內(nèi)存的獨(dú)占訪問權(quán)限.如果編譯器能夠證明對(duì)內(nèi)存的非獨(dú)占訪問仍然是安全的疹蛉,那么Swift允許這種內(nèi)存安全的代碼。具體而言力麸,如果滿足以下條件可款,則可以證明對(duì)結(jié)構(gòu)屬性的重疊訪問是安全的:

只訪問實(shí)例的存儲(chǔ)屬性,而不是計(jì)算屬性或類屬性克蚂。
結(jié)構(gòu)是局部變量的值闺鲸,而不是全局變量黎棠。
該結(jié)構(gòu)要么不被任何閉包捕獲肥橙,要么僅由non-escaping捕獲。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末璧南,一起剝皮案震驚了整個(gè)濱河市赤屋,隨后出現(xiàn)的幾起案子立镶,更是在濱河造成了極大的恐慌,老刑警劉巖类早,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件媚媒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡涩僻,警方通過查閱死者的電腦和手機(jī)缭召,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門栈顷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嵌巷,你說我怎么就攤上這事萄凤。” “怎么了搪哪?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵靡努,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我噩死,道長(zhǎng)颤难,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任已维,我火速辦了婚禮,結(jié)果婚禮上已日,老公的妹妹穿的比我還像新娘垛耳。我一直安慰自己,他們只是感情好飘千,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布堂鲜。 她就那樣靜靜地躺著,像睡著了一般护奈。 火紅的嫁衣襯著肌膚如雪缔莲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天霉旗,我揣著相機(jī)與錄音痴奏,去河邊找鬼。 笑死厌秒,一個(gè)胖子當(dāng)著我的面吹牛读拆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鸵闪,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼檐晕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蚌讼?” 一聲冷哼從身側(cè)響起辟灰,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎篡石,沒想到半個(gè)月后芥喇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夏志,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年乃坤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苛让。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡湿诊,死狀恐怖狱杰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厅须,我是刑警寧澤仿畸,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站朗和,受9級(jí)特大地震影響错沽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜眶拉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一千埃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忆植,春花似錦放可、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至拾氓,卻和暖如春冯挎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咙鞍。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工房官, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奶陈。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓易阳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親吃粒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子潦俺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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