読者です 読者をやめる 読者になる 読者になる

tocsatoの備忘録

ほぼほぼ 50 代のプログラマの備忘録。swift golang javascript css html5 nginx mysql などを最近使ってます。

swift3 で UnsafePointer

swift

swift 3.0 で UnsafePointer を使うサンプルの備忘録です。
そもそも swift 自体かなりの初心者なので、大いに間違いがあるかもしれません。
ツッコミ歓迎です。というか、教えてください。

swift 3.0 のポインタは何が変わった?

swift 3.0 ではポインタ周りが変更されました。
UnsafeRawPointer が導入されたり、Unmanaged が変更されたり...。
詳細は、
github.com
github.com
を参照してください。

ちなみに UnsafeRawPointer はこれまで UnsafePointer<Void> で表現されていたものです。

オブジェクトを UnsafeRawPointer でやりとりする

出典元: pointers - How to cast self to UnsafeMutablePointer<Void> type in swift - Stack Overflow

// ツール関数群
func bridge<T: AnyObject>(obj: T) -> UnsafeMutableRawPointer {
    return Unmanaged.passRetained(obj).toOpaque()
}

func bridge<T: AnyObject>(ptr: UnsafeRawPointer) -> T {
    return Unmanaged<T>.fromOpaque(ptr).takeRetainedValue()
}

// サンプルクラス
class Foo {
    private let id: Int

    init(id: Int) {
        self.id = id
    }

    func ID() -> Int {
        return self.id
    }
}

// Foo オブジェクト を作成する
var f = Foo(id: 3)

// オブジェクトのポインタを UnsafeMutableRawPointer で返す
var p = bridge(obj: f)

// UnsafeMutableRawPointer が指すポインタからオブジェクトを得る
var x: Foo = bridge(ptr: p)

// 確認
x.ID()    // 3

オブジェクトを UnsafePointer でやりとりする

どうも上の bridge() が複雑なのが不可解で、自分の理解の範囲で書いてみます。
inout 引数が UnsafePointer と互換がある (?) のを利用しています。

struct Bar {
    var x: Int
    var y: Int
}

class Foo {
    private let id: Int
    
    init(id: Int) {
        self.id = id
    }
    
    func ID() -> Int {
        return self.id
    }
}

// Bar, Foo のオブジェクトを作成する
var b = Bar(x:7, y:5)
var f = Foo(id: 3)

// ツール関数
func ptr<T: Any>(p: UnsafePointer<T>) -> UnsafePointer<T> {
    return p
}

// Boo 構造体のポインタを取得する
var p2 = ptr(p: &b)

// ポインタの先のpointee にオブジェクトがある
p2.pointee.x    // 7
p2.pointee.y    // 5

// Foo オブジェクトのポインタを取得する
var p3 = ptr(p: &f)

// ポインタの先のpointee にオブジェクトがある
p3.pointee.ID()    // 3

// ちなみにこれはダメ (コンパイルエラーになる)
// var p4: UnsafePointer<Bar> = &b

UnsafeRawPointer と UnsafePointer を相互変換する

// UnsafeMutablePointer<UInt8> を作成する
let b = UnsafeMutablePointer<UInt8>.allocate(capacity: 4)
b.advanced(by: 0).pointee = 0x01    // b[0] = 0x01
b.advanced(by: 1).pointee = 0x00    // b[1] = 0x00
b.advanced(by: 2).pointee = 0x00    // b[2] = 0x00
b.advanced(by: 3).pointee = 0x80    // b[3] = 0x80

// UnsafeMutablePointer<> を UnsafeMutableRawPointer に変換する
let p = UnsafeRawPointer(b)

// UnsafeMutableRawPointer を UnsafeMutablePointer<Int16> に変換する
let v: UnsafePointer<Int16> = p.bindMemory(to: Int16.self, capacity: 2)
v.advanced(by: 0).pointee    // v[0] = 1
v.advanced(by: 1).pointee    // v[1] = -32768