內存安全

  1. 在inout參數的函數
    1. inout參數的變量會在函數內進行長期的寫訪問,如果在這個期間函數內部訪問了外部的這個相同的內存將會報錯,發生運行時的錯誤。
    2. 解決辦法1是在函數外將這個內存的值複製一份給兩一個變量,這樣在函數內部訪問這個變量即可,因爲現在訪問的將會是另一個內存。
    3. 解決辦法2是在函數內部不訪問外部的這個相同的內存,只是用傳入的參數。
// 報錯示例
var stepSize = 1
func increment(_ number: inout Int) {
     number = stepSize + number
}
increment(&stepSize)

// 修改方法1
var stepSize = 1
var size  = stepSize
func increment(_ number: inout Int) {
    number = size + number
}
increment(&stepSize)

// 修改方法2
var stepSize = 1
func increment(_ number: inout Int) {
    number = number + number
}
increment(&stepSize)
  1. 方法裏self的訪問衝突
func change(a: inout Int, b: inout Int){
    let sum = a + b
    a = sum/2
    b = sum - a
}

struct Player {
    var name: String
    var health: Int
    var energy: Int
    
    static let maxHealth = 10
    mutating func restoreHealth() {
        self.health = Player.maxHealth
    }
}

extension Player {
    mutating func shareHealth(player: inout Player) {
        change(a: &health, b: &player.health)
    }
}

var a = Player(name: "小明", health: 10, energy: 10)
var b = Player(name: "小紅", health: 3, energy: 5)
a.shareHealth(player: &b)
print("a.health = \(a.health)   b.health = \(b.health)")

a.shareHealth(player: &a)//報錯
  1. 屬性的訪問衝突
    1. 結構體屬於值類型,對於結構體中的某個變量的訪問其實要訪問整個結構體。
    2. 如果一個方法要求傳入的參數都是inout類型,並且都是一個結構體的兩個不同的參數。那麼就要對整個結構體進行訪問。如果這個結構體是全局的,那麼就是要對整個結構體進行長期寫訪問,就會報錯。如果這個結構體是局部的,那麼就是短期的寫訪問不報錯。
func change(a: inout Int, b: inout Int){
    let sum = a + b
    a = sum/2
    b = sum - a
}

struct Player {
    var name: String
    var health: Int
    var energy: Int
    
    static let maxHealth = 10
    mutating func restoreHealth() {
        self.health = Player.maxHealth
    }
}

extension Player {
    mutating func shareHealth(player: inout Player) {
        change(a: &health, b: &player.health)
    }
}

//以下報錯
//var a = Player(name: "小明", health: 10, energy: 10)
//change(a: &a.health, b: &a.energy)

//以下不報錯
func test() {
    var a = Player(name: "小明", health: 10, energy: 10)
    change(a: &a.health, b: &a.energy)
}
  1. 總結
    1. 對於值類型
      1. 如結構體用let修飾以後。自身不可變,它中的所有變量變爲不可變。
      2. 如果用var修飾以後,那麼自身是可以變的,結構體中的變量常量依然不做變化。
    2. 對於引用類型
      1. 如果用let修飾,那麼自身是不可以變的。它的所有屬性不變。常量還是常量,變量還是變量。
      2. 如果用var修飾,那麼自身是可以變的。它的所有屬性不變。常量還是常量,變量還是變量。
    3. 方法參數
      0. 其實方法的參數默認給了let類型!!!
      1. 如果是值類型,如結構體,枚舉,int,string。這時候默認是let修飾的。那麼自身是不可變的,它中的所有屬性也是常量不可變的。如果想變就要用inout進行修飾。可以理解爲inout把let變爲了var。
      2. 如果是引用類型,默認是let修飾的。那麼它自身是不可變的。它中的所有屬性保持不變。常量還是常量,變量還是變量。如果想要自身改變,那就用inout修飾
      3. 其實方法中的參數默認是let修飾的(隱藏起來了,對我們不可見),用inout修飾以後形參變爲了var類型。
    4. 寫訪問
      1. inout類型的參數要進行寫訪問。如果傳入這個參數是全局變量,就變成了對這個變量的長期寫訪問。在這個長期寫訪問期間,不能在進行訪問這塊內存,不然會報錯。(但是可以通過局部變量的形式進行訪問,不會報錯)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章