Swift3のポインタの実践編
Feb 24, 2017 · ios · macswift3
主にSwiftのポインタとCのポインタとの対比やポインタの変換方法についてのまとめ
Swiftでのポインタの基礎については基礎編を参照
Cのポインタとの対比
Hogeという型のポインタを表す場合
| Swift | C |
|---|---|
| UnsafePointer<Hoge> | const Hoge* |
| UnsafeMutablePointer<Hoge> | Hoge* |
void*(汎用ポインタ)には専用の型が用意されている
| Swift | C |
|---|---|
| UnsafeRawPointer | const void* |
| UnsafeMutableRawPointer | void* |
なお、ポインタ経由で値を変更したい場合はMutableがついている方を、参照のみであればついていない方を使う
ポインタへの変換方法
早見表
| 変換元 | Hoge | UnsafePointer | UnsafeRawPointer |
|---|---|---|---|
| Hoge | - | 1.withUnsafePointer | 1 -> 3 |
| UnsafePointer | 2.pointee or [] | - | 3.UnsafeRawPointer() |
| UnsafeRawPointer | 4 -> 2 | 4.assumingMemoryBound | - |
Mutableの場合も変換方法は同じであるが、それぞれMutableに対応したものを使う
HogeからUnsafeRawPointerのように直接変換できる方法がない場合は、
UnsafePointerに変換してから目的の型へ2段階で変換する
異なる型同士での変換については基礎編を参照のこと
コード例
前提として、Hogeは以下の型とする
struct Hoge {
var x: Float, y: Float, z: Float
}
(今回は構造体だが、実体がプリミティブでもクラスでも方法は同じ)
1.2. Hoge => Unsafe(Mutable)Pointer => Hoge
Hogeをポインタへ変換、変換したポインタ経由で元のHogeにアクセスする場合
Cの書き方
Hoge pos;
pos.x = pox.y = pos.z = 1;
Hoge* p = &pos;
p->x = 100; // pos.x = 100 となる
(*p).y = 200; // pos.y = 200 となる
Swiftの書き方
var pos = Hoge(x: 1, y: 1, z: 1)
withUnsafeMutablePointer(to: &pos) {
let p: UnsafeMutablePointer<Hoge> = $0
p.pointee.x = 100 // pos.x = 100 となる
p.pointee.y = 200 // pos.y = 200 となる
}
// または以下でも同じ
withUnsafeMutablePointer(to: &pos) { p in
// p が UnsafeMutablePointer<Hoge> となる
}
withUnsafe(Mutable)Pointerのクロージャは値を返せるからといって
let p = withUnsafeMutablePointer(to: &pos) { $0 } // 絶対ダメ
p.pointee.x = 100
というようにポインタを返す書き方は禁止。 渡されるポインタが有効なのはクロージャ内だけなので、上記書き方の動作は未定義である
よって、クロージャ内で処理を完結させるか、Swiftの変数に代入(値コピー)してそれを返すこと
3. Unsafe(Mutable)Pointer => Unsafe(Mutable)RawPointer
Cの書き方
// pHoge が Hoge* の場合
void* pRaw = (void*)pHoge;
Swiftの書き方
// pHoge が UnsafePointer<Hoge> の場合
let pRaw = UnsafeRawPointer(pHoge)
4. Unsafe(Mutable)RawPointer => Unsafe(Mutable)Pointer
Cの書き方
// pRaw が void* の場合
Hoge* pHoge = (Hoge*)pRaw;
Swiftの書き方
// pRaw が UnsafeRawPointer の場合
let pHoge = pRaw.assumingMemoryBound(to: Hoge.self)
ただし、pRawが既にバインド済であることが前提
未バインドの場合は、bindMemoryを使う
