[Swift ドキュメントの読み込み]メモリセキュリティ


こんにちは!エリンです🙇🏻‍♀️
SWIFT公式ドキュメント精読シリーズ!
私はもう私のスタイルに合わせて整理したので、もっと情報が必要です.
文書リンクを押して確認してください!
もっと快適に見るために、韓国語翻訳サイトを一緒に確認しました!ほほほ
では、始めましょう.
この文章は勉強しながら書いたので、間違った情報があるかもしれません.🥺
すぐ忘れちゃうよ.これは未来の私にもう一度伝えるためです.
間違ったところがあればコメントで親切に教えてください🙏

メモリ安定性


デフォルトでは、SWIFTはコードの異常な動作を阻止します.たとえば、変数が初期化前に使用されたり、メモリにアクセスしたりして解放された値がインデックスの制限を超えようとします.SWIFTはまた、メモリ位置を変更するコードがメモリに独占的にアクセスできることを要求し、同じメモリ領域への複数のアクセス競合を回避する.
これは、メモリ管理が自動的に管理されるため、ほとんどの場合、メモリアクセスを考慮する必要はありません.しかし、メモリアクセス競合が発生する可能性のある潜在的な状況を理解し、メモリアクセス競合を回避するコードをどのように記述するかを理解することが重要です.
(メモリアクセスの競合が発生した場合、ランタイムエラーまたはコンパイルエラーが発生します.)

メモリアクセスの競合について


コードでは、メモリアクセスは変数に値を割り当てたり、アクセスしたりするときに発生します.
// 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)!")
コードの他の部分がメモリの同じ場所に同時にアクセスしようとすると、メモリアクセスの競合が発生する可能性があります.メモリ内の場所に同時に複数回アクセスすると、予知できない操作や一致しない操作が発生する可能性があります.すなわち、メモリ競合は、値の割り当てと同時に値にアクセスするときに発生します.
(同期コードまたはマルチスレッドコードを記述したことがある場合は、メモリアクセスの競合が一般的な問題です.)ただし、このアクセス競合は、同期性やマルチスレッドに関係なく、単一スレッドで発生する可能性のある問題です.)

メモリアクセスの機能


次の3つの条件のうち2つを満たすと、特定のメモリ競合が発生する可能性があります.
  • 少なくとも1つの書き込み方法
  • メモリが同じ場所にアクセスする場合
  • アクセス継続時間重複時
  • 書き込みアクセスはメモリの場所を変更し、読み取りは変更しません.
    メモリの場所は、参照が何であるかを示します.
    メモリ・アクセスの持続時間は、インスタント・アクセスと長期アクセスに分けられます.
    インスタント・アクセスとは、コードがメモリ・アクセスを開始および終了するまで、メモリへのアクセスを開始できないことを意味します.
      func oneMore(than number: Int) -> Int {
          return number + 1
      }
    
      var myNumber = 1
      myNumber = oneMore(than: myNumber)
      print(myNumber)
      // Prints "2"
    上記の状況はインスタントアクセスであり、メモリアクセスの競合は発生しません.
    (ほとんどのメモリアクセスはインスタントです.)
    逆に、長期アクセスは、長期アクセスの開始および終了前に、オーバーラップと呼ばれる他のコードを実行することができる.

    入出力パラメータの競合アクセス


    関数には、すべての入出力パラメータに対する長期書き込みアクセス権があります.
    入出力パラメータへの書き込みアクセスは、すべての非入出力パラメータを計算した後に開始され、関数呼び出しの全過程で継続されます.複数の入出力パラメータがある場合、書き込みアクセスはパラメータが表示される順序で開始されます.
    var stepSize = 1
    
    func increment(_ number: inout Int) {
        number += stepSize
    }
    
    increment(&stepSize)
    // Error: conflicting accesses to stepSize

    Inout Intのnumberをincrementのパラメータとして使用します.次に、関数でパラメータとして使用されるnumberを変更します.この場合、numberをパラメータとして読み出し、numberを読み出してstep sizeをnumberに追加することで、書き込みと読み出しが同時に発生し、アクセス競合を引き起こす.
    // Make an explicit copy.
    var copyOfStepSize = stepSize
    increment(&copyOfStepSize)
    
    // Update the original.
    stepSize = copyOfStepSize
    // stepSize is now 2
    この問題を解決する1つの方法はstep Sizeのコピーを明示的に使用することである.ステップSizeのcopyOfStep Sizeをコピーすると、メモリを読み書きしながらアクセス競合を回避できます.
    func balance(_ x: inout Int, _ y: inout Int) {
        let sum = x + y
        x = sum / 2
        y = sum - x
    }
    var playerOneScore = 42
    var playerTwoScore = 30
    balance(&playerOneScore, &playerTwoScore)  // OK
    balance(&playerOneScore, &playerOneScore)
    // Error: conflicting accesses to playerOneScore
    同じ変数を複数の入出力パラメータの引数として同じ関数に渡すと、競合が発生します.
    (演算子も関数であるため、入出力パラメータへの長期的なアクセスが競合します.)

    メソッドでselfを使用するときに発生する競合


    構造体のmutatingメソッドのselfは、呼び出し中に書き込み権限を有する.
    struct Player {
        var name: String
        var health: Int
        var energy: Int
    
        static let maxHealth = 10
        mutating func restoreHealth() {
            health = Player.maxHealth
        }
    }
    
    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とMariaはそれぞれ異なる構造体の例であるため,体力を共有しても問題はない.
    しかし、オスカーが自分と同じ実例のオスカーと体力を共有したらどうなるのだろうか.
    oscar.shareHealth(with: &oscar)
    // Error: conflicting accesses to oscar

    1つのメモリ位置で読み取りと読み取りの体力を同時に実行する動作は、競合します.

    プロトコル・アクセスの競合

    var playerInformation = (health: 10, energy: 20)
    balance(&playerInformation.health, &playerInformation.energy)
    // Error: conflicting access to properties of playerInformation
    
    var holly = Player(name: "Holly", health: 10, energy: 10)
    balance(&holly.health, &holly.energy)  // Error
    
    func someFunction() {
        var oscar = Player(name: "Oscar", health: 10, energy: 10)
        balance(&oscar.health, &oscar.energy)  // OK
    }
    タイプ(構造体、継ぎ手、列挙など)は値タイプであるため、値を変更すると値全体が変更されます.したがって、tupleの要素を現在のように同じ関数で同時に呼び出すと、重複書き込みにアクセスするため、競合が発生します.
    (????公式文書を再確認…!)
    構造体でPropertyにアクセスする場合、以下の場合、上書きによるアクセスは安全です.
  • 構造体インスタンスがストレージプロセスのみにアクセスし、計算プロセスまたはクラスプロセスにアクセスしない場合
  • .
  • 構造体がグローバル変数ではなく領域変数である場合、
  • .
  • 構造体は、いかなるキャビネットからもキャプチャされていないか、または非キャビネットからのみ取得されている.
    コンパイラがアクセスが安全ではないと判断した場合、アクセス自体は安全ではありません.
  • 今日もSweetの公式文書を整理しました~
    次も頑張ります!
    ありがとうございます.🙇🏻‍♀️