溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Swift中發(fā)生內(nèi)存訪問沖突的情況有哪些

發(fā)布時(shí)間:2021-01-13 14:34:55 來源:億速云 閱讀:367 作者:Leah 欄目:開發(fā)技術(shù)

Swift中發(fā)生內(nèi)存訪問沖突的情況有哪些?相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

內(nèi)存訪問沖突

當(dāng)你設(shè)值或者讀取變量的值得時(shí)候,就會(huì)訪問內(nèi)存。

var age = 10 // 寫權(quán)限
print(age) // 讀權(quán)限

當(dāng)我們對(duì)同一塊內(nèi)存,同時(shí)進(jìn)行讀寫操作時(shí),會(huì)產(chǎn)生不可預(yù)知的錯(cuò)誤。比如上面的 age,假如在你讀取它值的期間有別的代碼將它設(shè)為 20,那么你讀取到的有可能是 10,也有可能是 20。這就產(chǎn)生了問題。

內(nèi)存訪問沖突:對(duì)同一塊內(nèi)存,同時(shí)進(jìn)行讀寫操作,或者同時(shí)進(jìn)行多個(gè)寫入操作時(shí),就會(huì)造成內(nèi)存訪問沖突。

了解了什么是內(nèi)存訪問沖突,下面來看下什么情況下回造成內(nèi)存訪問沖突。

In-Out 參數(shù)

當(dāng) In-Out 參數(shù)為全局變量,并且該變量在函數(shù)體內(nèi)被修改時(shí),就會(huì)造成內(nèi)存訪問沖突。比如下面的代碼:

var age = 10

func increment(_ num: inout Int) { // step1
 num += age // step2
}
increment(&age)

increment(:) 在整個(gè)函數(shù)體內(nèi),對(duì)所有的 In-Out 參數(shù)都有寫權(quán)限。在上述代碼中,step1 已經(jīng)獲得了 age 的寫權(quán)限,而 step2 有得到了 age 的讀權(quán)限,這樣就造成了同一塊內(nèi)存,同時(shí)進(jìn)行了讀寫操作。從而造成了內(nèi)存訪問沖突。

上面的問題可以通過將 age 拷貝一份來解決:

// step1
var copyOfAge = age
increment(&copyOfAge)
age = copyOfAge

step1 將 age 的值拷貝到另一塊內(nèi)存上,這樣在函數(shù)體內(nèi)就是存在對(duì) age 的讀權(quán)限和對(duì) copyOfAge 的寫權(quán)限,因?yàn)?age 和 copyOfAge 是兩塊內(nèi)存,所以就不會(huì)造成內(nèi)存訪問沖突。

結(jié)構(gòu)體的 mutating 函數(shù)

對(duì)于結(jié)構(gòu)體的 mutating 函數(shù)來說,它整個(gè)函數(shù)體都有 self 的寫權(quán)限。

struct Person {
 var age: Int
 mutating func increment(_ num: inout Int) { 
  age += num 
 }
}

var p1 = Person(age: 10)
p1.increment(&p1.age)

上述的代碼編譯器會(huì)報(bào)錯(cuò):Overlapping accesses to 'p1', but modification requires exclusive access; consider copying to a local variable。很明顯這是一個(gè)內(nèi)存訪問沖突。

In-Out 參數(shù)獲得了 p1 的寫權(quán)限;mutating 函數(shù)也獲得了 p1 的寫權(quán)限。同一塊內(nèi)存,同時(shí)有兩個(gè)寫操作。造成內(nèi)存訪問沖突。可以通過同上的拷貝操作來解決。

值類型的屬性

對(duì)于結(jié)構(gòu)體、枚舉、元祖等值類型來說,修改它們的屬性就相當(dāng)于修改它們整個(gè)的值。比如下面的代碼:

func increment(_ num1: inout Int, _ num2: inout Int) {
 print(num1 + num2)
}

var tuple = (age: 10, height: 20)
increment(&tuple.age, &tuple.height)

&tuple.age 拿到了 tuple 的寫權(quán)限,&tuple.height 又拿了 tuple 的寫權(quán)限。同一塊內(nèi)存,同時(shí)有兩個(gè)寫操作。造成內(nèi)存訪問沖突。

這個(gè)問題可以通過局部變量來解決:

func someFunction() {
 var tuple = (age: 10, height: 20)
 increment(&tuple.age, &tuple.height)
}

因?yàn)樵?someFunction() 函數(shù)里,age 和 height 沒有產(chǎn)生任何的交互(沒有在其期間去讀取或者寫入 age 和 height),所以編譯器可以保證內(nèi)存安全。

PS:關(guān)于評(píng)論區(qū)的問題,在 someFunction() 函數(shù)里沒有任何交互是什么意思?

答:在someFunction() 里,編譯器可以保證沒有別的線程來讀取或者修改 tuple。因此,可以保證內(nèi)存安全。而對(duì)于全局變量,編譯器無法保證是否有別的線程在讀取或者修改。

下面的代碼就是在函數(shù)體內(nèi)有交互的代碼,雖然是局部變量,但涉及多個(gè)線程修改 tuple 的值,因此會(huì)造成內(nèi)存訪問沖突:

func someFunction() {
 var tuple = (age: 10, height: 20)
 
 DispatchQueue.main.async {
  tuple.age += 10
 }
 
 DispatchQueue.main.async {
  increment(&tuple.age, &tuple.height)
 }
}

總結(jié)

對(duì)同一塊內(nèi)存,同時(shí)進(jìn)行讀寫操作,或者同時(shí)進(jìn)行多個(gè)寫入操作時(shí),就會(huì)造成內(nèi)存訪問沖突。

會(huì)造成內(nèi)存訪問沖突的情況:

  • In-Out 為全局參數(shù),并且在函數(shù)體內(nèi)修改了它。

  • 結(jié)構(gòu)體的 mutating 函數(shù)內(nèi)修改結(jié)構(gòu)體的值。

  • 同一值類型的多個(gè)屬性當(dāng)做函數(shù)的 In-Out 參數(shù)。

看完上述內(nèi)容,你們掌握Swift中發(fā)生內(nèi)存訪問沖突的情況有哪些的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI