0Pricing
Swift Academy · レッスン

UnsafePointerとUnsafeMutablePointer

生メモリを慎重に読み書きします。

「UnsafePointerとUnsafeMutablePointer」はCoddyKit上の無料Swift Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSwift Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Swift Academyコースには全4レッスンが含まれています。

Swift の型付きポインター

Swift の安全な参照では生のアドレスを直接扱えませんが、C と相互運用したり低レベルのデータ構造を構築したりするときは、型付きポインターを使用します。読み取り専用アクセスには UnsafePointer<T>、読み書きには UnsafeMutablePointer<T> を使います。

型付きポインターは要素型を認識しているため、ストライドを計算し、適切な型の値を読み取れます。

// A pointer to an Int we can read but not write
func readFirst(_ p: UnsafePointer<Int>) -> Int {
    return p.pointee
}

pointee プロパティ

pointee は、ポインターが現在指している値です。変更可能なポインターでは読み取りと代入の両方ができますが、読み取り専用ポインターでは読み取りしかできません。

初期化されていないメモリや解放済みのメモリに対して pointee にアクセスすると、未定義動作になります。型システムでは検出できません。

let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
p.initialize(to: 42)
print(p.pointee)   // 42
p.pointee = 99
print(p.pointee)   // 99

メモリの確保

UnsafeMutablePointer<T>.allocate(capacity:) は capacity 個の要素を格納する領域を確保しますが、要素の初期化は行いません。メモリは生の状態であり、初期化前に読み取ることは不正です。

// Reserve space for 3 Doubles (uninitialized)
let buffer = UnsafeMutablePointer<Double>.allocate(capacity: 3)
// Must initialize before reading any element

initialize と deinitialize

読み取る前に initialize しなければなりません。クラスや文字列のようにクリーンアップが必要な型を解放する前には、リソースを解放するために deinitialize しなければなりません。

ライフサイクルは、allocate → initialize → use → deinitialize → deallocate です。

let p = UnsafeMutablePointer<String>.allocate(capacity: 1)
p.initialize(to: "hello")
print(p.pointee)
p.deinitialize(count: 1) // releases the String
p.deallocate()

allocate と deallocate は必ず対にする

すべての allocate は、ちょうど1回の deallocate と対にしなければなりません。そうしないとメモリリークが発生します。確保直後に defer を使うのが最も安全なパターンです。関数がどのように終了しても実行されるためです。

func process() {
    let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer { p.deallocate() }
    p.initialize(to: 7)
    print(p.pointee)
} // deallocate runs here automatically

ポインター演算

型付きポインターのインデックス指定と演算は、バイト単位ではなく要素単位で行われます。p + 2 は T のストライド2つ分だけ進みます。p[i] は (p + i).pointee の省略記法です。

let p = UnsafeMutablePointer<Int>.allocate(capacity: 3)
p.initialize(repeating: 0, count: 3)
p[0] = 10
p[1] = 20
p[2] = 30
print((p + 1).pointee) // 20
p.deinitialize(count: 3)
p.deallocate()

複数の要素を initialize する

メモリブロックに対して、initialize(repeating:count:) はすべてのスロットを同じ値に設定し、initialize(from:count:) は別のバッファーからコピーします。どちらも範囲全体を初期化するため、安全に読み取れます。

let dst = UnsafeMutablePointer<Int>.allocate(capacity: 4)
dst.initialize(repeating: 5, count: 4)
print(dst[3]) // 5
dst.deinitialize(count: 4)
dst.deallocate()

読み取り専用と変更可能

データを調べるだけの関数は UnsafePointer<T> を受け取り、データを変更する関数は UnsafeMutablePointer<T> を受け取るべきです。変更可能なポインターから読み取り専用ポインターへの変換は暗黙的に行えますが、逆方向はできません。これにより、API の境界で意図が保証されます。

func sum(_ p: UnsafePointer<Int>, _ n: Int) -> Int {
    var total = 0
    for i in 0..<n { total += p[i] }
    return total
}
// A mutable pointer can be passed here directly

ローカル値に withUnsafePointer を使う

メモリを確保せずに既存の値への一時的なポインターを取得するには、withUnsafePointer(to:) またはその変更可能な形式を使います。ポインターが有効なのはクロージャ内だけなので、決して外に持ち出してはいけません。

var value = 100
withUnsafeMutablePointer(to: &value) { ptr in
    ptr.pointee += 1
}
print(value) // 101

よくある落とし穴

型付きポインターは強力ですが、扱いには細心の注意が必要です:

  • initialize 前に pointee を読み取ると未定義動作になります。
  • リソースを保持する型で deinitialize を忘れるとリークが発生します。
  • withUnsafePointer のポインターを外に持ち出すと、ダングリングポインターになります。

ここではコンパイラーがあなたを信頼するため、実行時の安全装置はありません。

// DANGEROUS — pointer escapes the closure
// var escaped: UnsafePointer<Int>?
// withUnsafePointer(to: &x) { escaped = $0 }
// escaped!.pointee  // dangling, undefined behavior

小さな手動スタック

ここまでの内容を組み合わせると、メモリブロックを確保し、インデックスで要素として扱い、最後に後片付けできます。これはカスタムコレクションや C との相互運用の基礎になります。

let n = 3
let stack = UnsafeMutablePointer<Int>.allocate(capacity: n)
stack.initialize(repeating: 0, count: n)
for i in 0..<n { stack[i] = i * i }
// stack now holds 0, 1, 4
stack.deinitialize(count: n)
stack.deallocate()

確認問題

正しいポインターのライフサイクルを思い出しましょう。

まとめ

型付きポインターについて学びました:

  • UnsafePointer は読み取り、UnsafeMutablePointer は読み取りと書き込みを行います。
  • pointee で値にアクセスし、演算は要素単位で行います。
  • ライフサイクルは allocate → initialize → use → deinitialize → deallocate であり、defer で保護するのが理想的です。

次は、バッファーポインターが連続した領域全体を Collection と親和性の高い API でラップする方法を見ていきます。

よくある質問

「UnsafePointerとUnsafeMutablePointer」レッスンは無料ですか?

はい。「UnsafePointerとUnsafeMutablePointer」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Swift Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Swift Academyコースには全4レッスンが含まれています。

「UnsafePointerとUnsafeMutablePointer」で何を学びますか?

生メモリを慎重に読み書きします。 ブラウザで直接実行するハンズオンコードでSwift Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Swift Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSwift Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「UnsafePointerとUnsafeMutablePointer」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSwift Academyレッスンでコードを書いて実行できますか?

はい。すべてのSwift Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. MemoryLayoutとアライメント
  2. UnsafePointerとUnsafeMutablePointer
  3. Unsafeバッファポインタ
  4. withUnsafeBytesとC相互運用
← Swift Academyに戻る