実際には、Swiftはまた、ポインタの特別な型を持っており、そのようなKVOの使用のようにどこでも見つけることができますUnsafeMutableRawPointerは、C言語のvoid *と同等であることがわかります。
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
}
SwiftのポインタはUnsafeで、2つの種類があります。
ポインタの指す先を変更する MutablePointer。
- UnsafeMutableRawPointer C に似たユビキタスでないクラス void *
ポインタを介して変数の内容を変更したり表示したりするだけなら、それが可能です。引数である変数への変数ポインタを持つ関数を宣言し、このポインタを使用して変数のメモリを変更したりアクセスしたりすることができます。
var str = "Hello, playground"
func test1(_ ptr :UnsafeMutablePointer<String>){
ptr.pointee += " Swift"//ジェネリッククラスのポインタは、クラスの公差を指すようにポインタを変更する
}
func test2(_ ptr :UnsafeMutableRawPointer){
ptr.storeBytes(of: "Hello, Swift", as: String.self)//非ジェネリッククラスのポインタが指すポインタの内容を変更する
}
test1(&str)
print(str)//Hello, playground Swift
test2(&str)
print(str)//Hello, Swift
もうひとつは、ポインタの指す先を変更できないポインタ型です。
使用法:不変ポインターを入力パラメーターとする関数を宣言し、そのポインターが指す変数のメモリーにアクセスします。
func test3(_ ptr :UnsafePointer<String>){
print(ptr.pointee)//このポインタへの参照にアクセスする一般的な不変ポインタの例。
}
var name = "Anthony"
test3(&name)//Anthony
func test4(_ ptr :UnsafeRawPointer) {
print(ptr.load(as: String.self))//このポインタ参照例にアクセスするための非一般的な不変ポインタ。
}
test4(&name)//Anthony
変数へのポインタの取得
関数をUnsafeMutablePointerで呼び出すと、次のように対応する変数へのポインタを取得できます。
var score = 100
var ptr1 = withUnsafeMutablePointer(to: &score) { (point) -> UnsafeMutablePointer<Int> in
return point
}
//と略すことができる。
var ptr2 = withUnsafeMutablePointer(to: &score){$0}
print("ptr1===",ptr1)//ptr1=== 0x000000010bda1530
print("ptr2===",ptr2)//ptr2=== 0x000000010bda1530
ptr1.pointee = 120//変数ポインタの値、参照のパワーを取得するために変更する
var ptr3 = withUnsafeMutablePointer(to: &score) { UnsafeMutableRawPointer($0)//汎用ポインタを非汎用ポインタに変換する。
}
ptr3.storeBytes(of: 150, as: Int.self)
ヒープ空間内のクラス・オブジェクトのアドレス値を取得します。
ヒープ空間にあるクラス・オブジェクトのアドレス値を取得するには、3つのステップがあります。
- オブジェクトのメモリー・アドレスへのポインター変数を取得します。
- ポインタが指すアドレスの値を削除します。
- メモリ上のオブジェクトのアドレスは、ポインタが指すアドレスから取得されます。
var student = Student.init(age: 20)
var ptr = withUnsafePointer(to: &student) { UnsafeRawPointer($0) }//まず、student変数へのポインタを取得する。
let address = ptr.load(as: UInt.self)//ptrが指すStudentオブジェクトの値をメモリから取り出す。
var objectPtr = UnsafeRawPointer(bitPattern: address)//これはヒープ空間における生徒オブジェクトのアドレスだ。
print(objectPtr)//Optional(0x0000600001866120)
ポインタの作成
既に存在する変数へのポインタを取得する必要がありますが、ここではポインタの作成方法を学ぶ必要があります。Intを格納するだけなら、mallocを使ってメモリブロックを確保し、それをポインタ変数に指すだけです。
var ptr = malloc(24)//サイズ24バイトのメモリブロックを確保し、アドレス値を変数ptrに代入する。
//
ptr?.storeBytes(of: 2020, as: Int.self)//最初の8バイトは2020年に格納される
ptr?.storeBytes(of: 08, toByteOffset: 8, as: Int.self)//8-16バイトアクセスで 08
ptr?.storeBytes(of: 01, toByteOffset: 16, as: Int.self)//16-24バイトアクセスで01
//
print((ptr?.load(as: Int.self))!) //2020
print((ptr?.load(fromByteOffset: 8, as: Int.self))!) // 8
print((ptr?.load(fromByteOffset: 16, as: Int.self))!) // 16
free(ptr)//メモリのこの部分はARCによって管理されていないので、手動で破棄する必要がある。
様々なデータ型へのポインタを生成したい場合は、前述のUnsafeMutablePointerを使う必要があります。
//ポインターを作る
var genericPtr = UnsafeMutablePointer<String>.allocate(capacity: 3)//文字列型のポインタを生成し、文字列の型のサイズの容量は、Swiftの文字列は16バイトを占有するなど、3つのサイズを格納することができますし、割り当てのサイズは48バイトであり、メモリは3つのセクションに分割される
//
genericPtr.initialize(to: "Hello")//メモリストレージの最初の部分 こんにちは
genericPtr.successor().initialize(to: "World")//successorを使って、次に割り当てられたメモリブロックにアクセスする。
genericPtr.successor().successor().initialize(to: "Swift")
// MARK: -値を取り出す方法は、C言語の配列とよく似ている。
print(genericPtr.pointee) // "Hello"
print((genericPtr + 1).pointee) // "World"
print((genericPtr + 2).pointee) // "Swift"
print(genericPtr[0]) // "Hello"
print(genericPtr[1]) // "World"
print(genericPtr[2]) // "Swift"
genericPtr.deinitialize(count: 3)
genericPtr.deallocate()//メモリを解放する
カスタムクラスへのポインタの作成
//ポインターを作る
var objcPtr = UnsafeMutablePointer<Teacher>.allocate(capacity: 3)//Teacherオブジェクトを3つ格納できるメモリを確保し、そのメモリアドレスをobjcPtrに代入する。
//
objcPtr.initialize(to: Teacher.init(gradeClass: "3グレード2", name: "李さん"))
objcPtr.successor().initialize(to: Teacher.init(gradeClass: "2グレード1", name: "王さん"))
(objcPtr + 2).initialize(to: Teacher.init(gradeClass: "1グレード5", name: "張さん"))
//
print(objcPtr.pointee.description)//32年Liクラス教師
print(objcPtr.successor().pointee.description)//2年1組 王さん
print(objcPtr.successor().successor().pointee.description)//15年生張先生
//メモリを解放する
objcPtr.deinitialize(count: 3)//3つの教師オブジェクトが格納される
objcPtr.deallocate()//メモリを解放する
ポインタ変換
任意の型のポインタを宣言し、それを特定の型のポインタに変更したい場合は、unsafeBitCastを使用して強制的に変換することができます、次の例。
class Father {
var name = "WangDaDog"
init() {
}
}
class Son {
var name = "王子犬"
init() {
}
}
var ptr = UnsafeMutableRawPointer.allocate(byteCount: 64, alignment: 1)//64バイトのメモリブロックを割り当てる
ptr.assumingMemoryBound(to: Son.self).pointee = Son.init()//割り当てられた息子の最初の32バイト
(ptr + 32).assumingMemoryBound(to: Father.self).pointee = Father.init() //最後の32バイトは父に割り当てられる
print(unsafeBitCast(ptr, to: UnsafePointer<Son>.self).pointee.name)//このポインターはもともとvoidのような任意の型だった。 * これでUnsafePointerに変換される。<Son>
//印刷結果子犬王
print(unsafeBitCast(ptr + 32 , to: UnsafePointer<Father>.self).pointee.name)//任意の型のポインタをUnsafePointerに変換する。<Father>
//印刷結果 王大豪
// unsafeBitCast はデータ型の強制変換を無視し、データ型が変わっても元のメモリデータを変更しません。
//ptrポインタはメモリの最初の32バイトを指し、ストレージの内容はSonオブジェクトのインスタンスである、今、強制的にポインタの型をSonポインタの型を指すことから、Fatherポインタの型を指すように変更する。.
print(unsafeBitCast(ptr, to: UnsafePointer<Father>.self).pointee)//__lldb_expr_123.Son
//ポインタは父型に変わったが、メモリはまだ子型に格納されていることがわかる。.
ptr.deallocate()
ptrポインタが指すメモリの最初の32バイトは、Son型のインスタンスです。 ptrポインタをFather型のポインタに変換しても、メモリデータは変更されません。
ありますので、記事と照らし合わせてみてください。その方がわかりやすいです。
スウィフトポインタ最初のチャットはここに、任意のエラーや欠点がある、指摘する歓迎。私はあなたともっと議論し、交換することができることを願っています。