Swift中指针UnsafePointer的常见用法
指针类型
1 2 3 4 5 6 7 8 9 10 11 | //基本指针 UnsafePointer < T > const T * UnsafeMutablePointer T * //集合指针 UnsafeBufferPointer const T * //指向一个连续已知类型区域,可以看成一个集合,并支持集合操作 UnsafeMutableBufferPointer T * //指向一个连续已知类型区域,可以看成一个集合,并支持集合操作 //空指针 UnsafeRawPointer const void * UnsafeMutableRawPointer void * UnsafeRawBufferPointer const void * //指向一个连续未知类型区域 UnsafeMutableRawBufferPointer void * //指向一个连续未知类型区域 |
UnsafePointer 和 UnsafeMutablePointer
UnsafePointer只读指针
UnsafeMutablePointer可修改指针
UnsafePointer只读指针
通过UnsafePointer<T>定义一个类型为T的指针,通过pointee成员即可获得T的值。
1 2 3 4 5 6 7 | //UnsafePointer参数只能接收inout修饰的类型,而inout修饰的类型必然是可写的,所以参数只能用var定义 func call ( _ p : UnsafePointer < Int > ) { print ( "\( p . pointee )" ) } var a = 1234 //&a用于传递指针 call ( & a ) // 打印:1234 |
UnsafeMutablePointer可修改指针
可以通过设置p.pointee 修改指针的值。
1 2 3 4 5 6 | func modify ( _ p : UnsafeMutablePointer < Int > ) { p . pointee = 5678 } var a = 1234 modify ( & a ) print ( "\( a )" ) // 打印:5678 |
UnsafeBufferPointer 集合指针类型
UnsafeBufferPointer指针实现了Collection协议,因此可以直接使用Collection中的各种方法来遍历操作数据,filter,map...等。
Swift的数组提供了函数withUnsafeBufferPointer,通过它我们可以方便的用指针来处理数组。
下面通过withUnsafeBufferPointer获得变量p,p的类型为UnsafeBufferPointer<Int32>,它代表着一整块的连续内存,它是集合类型指针,并且它也支持大部分数组操作。
1 2 3 4 | let a : [ Int32 ] = [ 1 , 2 , - 1 , - 2 , 5 , 6 ] let p = a . withUnsafeBufferPointer { $ 0 } print ( "\( p . count )" ) // 打印:6 print ( "\( p [ 3 ] )" ) // 打印:-2 |
空指针:UnsafeRawPointer
就像C语言有void*(即空指针)一样,Swift也有自己的空指针,它通过类型UnsafeRawPointer来获得
1 2 3 4 | let rp : UnsafeMutablePointer < CChar > = rRes . value let strLength = rRes . length //将UnsafeMutablePointer<CChar> C语言的char *指针转成 UnsafeMutableRawPointer 的void *任意指针。然后直接取出封装成String字符串 let strRes = String . init ( bytesNoCopy : UnsafeMutableRawPointer ( mutating : rp ), length : Int ( strLength ), encoding : . utf8 , freeWhenDone : false ) |
指针的类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | UnsafePointer 基本指针 //将指针的类型转换成一个临时的给定类型,并将指针传递给closure func withMemoryRebound < T , Result > ( to : T . Type , capacity : Int , ( UnsafePointer < T > ) - > Result ) - > Result //向下移动一位,并返回一个新的指针 func successor () - > UnsafePointer < Pointee > //向上移动一位,并返回一个新的指针 func predecessor () - > UnsafePointer < Pointee > RawPointer 空指针 //转换给指定类型的指针 func assumingMemoryBound < T > ( to : T . Type ) - > UnsafeMutablePointer < T > //转换成指定类型的指针,capacity指定了这个指针读取的T数据数量 func bindMemory < T > ( to type : T . Type , capacity count : Int ) - > UnsafeMutablePointer < T > UnsafeBufferPointer 集合指针 let rpSub : UnsafeMutablePointer < UInt8 > = rp . withMemoryRebound ( to : UInt8 . self , capacity : Int ( strLength ), { $ 0 }) let buffer = UnsafeBufferPointer < UInt8 > . init ( start : rpSub , count : Int ( strLength )) |
指针的操作
指针的辅助函数,通过函数withUnsafePointer,获得指定类型的对应指针
1 2 3 4 5 | //返回一个T类型指针。将第一个参数T以指针的形式传递给closure func withUnsafePointer < T , Result > ( to : T , ( UnsafePointer < T > ) - > Result ) - > Result func withUnsafePointer < T , Result > ( to : inout T , ( UnsafePointer < T > ) - > Result ) - > Result func withUnsafeMutablePointer < T , Result > ( to : inout T , ( UnsafeMutablePointer < T > ) - > Result ) - > Result func withUnsafePointer < T , Result > ( to value : inout T , _ body : ( UnsafePointer < T > ) throws - > Result ) rethrows - > Result |
定义一个整形a,而p就是指向a的整形指针,它的类型会被自动转换为UnsafePointer<Int>,
第二个参数被简化为了{ $0 },代码块接收一个UnsafePointer<Int>参数,该参数即是a的地址,直接通过$0将它返回,即得到了a的指针,最终它被传给了p。
1 2 3 | var a = 1234 let p = withUnsafePointer ( to : & a ) { $ 0 } print ( "\( p . pointee )" ) // 打印:1234 |
获取指针并进行字节级操作 withUnsafeBytes
如果要对某块内存进行字节级编程。可以通过withUnsafeBytes得到某个类型的数据的字节指针,从而可以对它们进行字节级编程。
因为withUnsafeBytes返回了一个类型UnsafeRawBufferPointer,该类型代表着一个字节级的内存块集合指针,所以以通过下标索引、for循环的方式来处理返回的对象。
1 2 3 4 5 6 7 8 9 | var a : UInt32 = 0x12345678 let p = withUnsafeBytes ( of : & a ) { $ 0 } var log = "" for item in p { let hex = NSString ( format : "%x" , item ) log += "\( hex )" } print ( "\( p . count )" ) // 打印:4 print ( "\( log )" ) // 对于小端机器会打印:78563412 |
通过指针动态创建、销毁内存
使用Swift可以直接操作内存,如可以开辟和管理一块内存,最后释放它,Swift提供了UnsafeMutablePointer的成员函数allocate来处理该工作。
1 2 3 4 5 6 | let p = UnsafeMutablePointer < Int32 > . allocate ( capacity : 1 ) p . initialize ( to : 0 ) // 初始化 p . pointee = 32 print ( "\( p . pointee )" ) // 打印:32 p . deinitialize ( count : 1 ) // 反初始化 p . deallocate () |
C标准库函数的映射调用
Swift提供了大量的C标准库的桥接方法,我们可以像调用C语言库函数一样它们,如memcpy,strcpy等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | var n = 10086 // malloc let p = malloc ( MemoryLayout < Int32 > . size )! // memcpy memcpy ( p , & n , MemoryLayout < Int32 > . size ) let p2 = p . assumingMemoryBound ( to : Int32 . self ) print ( "\( p2 . pointee )" ) // 打印:10086 // strcpy let str = "abc" . cString ( using : . ascii )! if str . count != MemoryLayout < Int32 > . size { return } let pstr = p . assumingMemoryBound ( to : CChar . self ) strcpy ( pstr , str ) print ( "\( String ( cString : pstr ) )" ) // 打印:abc // strlen print ( "\( strlen ( pstr ) )" ) // 打印: 3 // memset memset ( p , 0 , MemoryLayout < Int32 > . size ) print ( "\( p2 . pointee )" ) // 打印:0 // strcat strcat ( pstr , "h" . cString ( using : . ascii )!) strcat ( pstr , "i" . cString ( using : . ascii )!) print ( "\( String ( cString : pstr ) )" ) // 打印:hi // strstr let s = strstr ( pstr , "i" )! print ( "\( String ( cString : s ) )" ) // 打印:i // strcmp print ( "\( strcmp ( pstr , " hi ".cString(using: .ascii)!))" ) // 打印:0 // free free ( p ) |
实际使用案例
1.将C函数库返回的char *指针,读取成字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | let rRes = c_shmread () let rp : UnsafeMutablePointer < CChar > = rRes . value let strLength = rRes . length //1.先将UnsafeMutablePointer<CChar> C语言的char *指针转成 UnsafeMutableRawPointer 的void *任意指针。然后直接取出封装成String字符串 if let strRes = String . init ( bytesNoCopy : UnsafeMutableRawPointer ( mutating : rp ), length : Int ( strLength ), encoding : . utf8 , freeWhenDone : false ) { return strRes } else { return "" } //2.使用withMemoryRebound转换指针类型从CChar转成UInt8类型; let rpSub : UnsafeMutablePointer < UInt8 > = rp . withMemoryRebound ( to : UInt8 . self , capacity : Int ( strLength ), { $ 0 }) //然后创建集合类型指针UnsafeBufferPointer; let buffer = UnsafeBufferPointer < UInt8 > . init ( start : rpSub , count : Int ( strLength )) buffer . forEach { print ($ 0 ) } //然后创建Data, 再将data转成String let data = Data ( bytes : rpSub , count : Int ( strLength )) let str = String ( data : data , encoding : String . Encoding . utf8 ) |
参考文章:
https://juejin.cn/post/7030789069915291661#heading-6
https://www.jianshu.com/p/a9956ee1ab61
· 分享4款.NET开源、免费、实用的商城系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了