Swift中發(fā)生內(nèi)存訪問(wèn)沖突的情況有哪些?相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
目前創(chuàng)新互聯(lián)公司已為千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬主機(jī)、網(wǎng)站托管維護(hù)、企業(yè)網(wǎng)站設(shè)計(jì)、平順網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。當(dāng)你設(shè)值或者讀取變量的值得時(shí)候,就會(huì)訪問(wèn)內(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)生了問(wèn)題。
內(nèi)存訪問(wèn)沖突:對(duì)同一塊內(nèi)存,同時(shí)進(jìn)行讀寫操作,或者同時(shí)進(jìn)行多個(gè)寫入操作時(shí),就會(huì)造成內(nèi)存訪問(wèn)沖突。
了解了什么是內(nèi)存訪問(wèn)沖突,下面來(lái)看下什么情況下回造成內(nèi)存訪問(wèn)沖突。
當(dāng) In-Out 參數(shù)為全局變量,并且該變量在函數(shù)體內(nèi)被修改時(shí),就會(huì)造成內(nèi)存訪問(wèn)沖突。比如下面的代碼:
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)存訪問(wèn)沖突。
上面的問(wèn)題可以通過(guò)將 age 拷貝一份來(lái)解決:
// step1 var copyOfAge = age increment(©OfAge) 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)存訪問(wèn)沖突。
對(duì)于結(jié)構(gòu)體的 mutating 函數(shù)來(lái)說(shuō),它整個(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)存訪問(wèn)沖突。
In-Out 參數(shù)獲得了 p1 的寫權(quán)限;mutating 函數(shù)也獲得了 p1 的寫權(quán)限。同一塊內(nèi)存,同時(shí)有兩個(gè)寫操作。造成內(nèi)存訪問(wèn)沖突??梢酝ㄟ^(guò)同上的拷貝操作來(lái)解決。
對(duì)于結(jié)構(gòu)體、枚舉、元祖等值類型來(lái)說(shuō),修改它們的屬性就相當(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)存訪問(wèn)沖突。
這個(gè)問(wèn)題可以通過(guò)局部變量來(lái)解決:
func someFunction() { var tuple = (age: 10, height: 20) increment(&tuple.age, &tuple.height) }
因?yàn)樵?someFunction() 函數(shù)里,age 和 height 沒(méi)有產(chǎn)生任何的交互(沒(méi)有在其期間去讀取或者寫入 age 和 height),所以編譯器可以保證內(nèi)存安全。
PS:關(guān)于評(píng)論區(qū)的問(wèn)題,在 someFunction() 函數(shù)里沒(méi)有任何交互是什么意思?
答:在someFunction() 里,編譯器可以保證沒(méi)有別的線程來(lái)讀取或者修改 tuple。因此,可以保證內(nèi)存安全。而對(duì)于全局變量,編譯器無(wú)法保證是否有別的線程在讀取或者修改。
下面的代碼就是在函數(shù)體內(nèi)有交互的代碼,雖然是局部變量,但涉及多個(gè)線程修改 tuple 的值,因此會(huì)造成內(nèi)存訪問(wèn)沖突:
func someFunction() { var tuple = (age: 10, height: 20) DispatchQueue.main.async { tuple.age += 10 } DispatchQueue.main.async { increment(&tuple.age, &tuple.height) } }
對(duì)同一塊內(nèi)存,同時(shí)進(jìn)行讀寫操作,或者同時(shí)進(jìn)行多個(gè)寫入操作時(shí),就會(huì)造成內(nèi)存訪問(wèn)沖突。
會(huì)造成內(nèi)存訪問(wèn)沖突的情況:
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)存訪問(wèn)沖突的情況有哪些的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!