加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Swift Pointer 使用指南

发布时间:2020-12-14 05:12:19 所属栏目:百科 来源:网络整理
导读:Overview C Syntax Swift Syntax Note const Type * UnsafePointerType 指针可变,指针指向的内存值不可变。 Type * UnsafeMutablePointerType 指针和指针指向的内存值均可变。 ClassType * const * UnsafePointerClassType 指针的指针:指针不可变,指针指

Overview

C Syntax Swift Syntax Note
const Type * UnsafePointer<Type> 指针可变,指针指向的内存值不可变。
Type * UnsafeMutablePointer<Type> 指针和指针指向的内存值均可变。
ClassType * const * UnsafePointer<ClassType> 指针的指针:指针不可变,指针指向的类可变。
ClassType * __strong * UnsafeMutablePointer<ClassType> 指针的指针:指针和指针指向的类均可变。
ClassType ** AutoreleasingUnsafeMutablePointer<Type> ?
const void * UnsafeRawPointer 指针指向的内存区类型未定。
void * UnsafeMutableRawPointer 同上
StructType * OpaquePointer C 语言中的一些自定义类型,Swift 中并未有相对应的类型。
int8_t a[] var x:[Int8]?->?UnsafeBufferPointer Buffer 一词不难联想到数组

对于声明为?UnsafePointer<Type>作为参数的函数,同样可以接受如下类型:

  • UnsafePointer<Type>,?UnsafeMutablePointer<Type>,?AutoreleaseingUnsafeMutablePointer<Type>,以上类型可转成?UnsafePointer<Type>
  • String值,如果Type为?Int8或?Uint8,那么字符串将被转成 UTF8 数组。要知道底层本质还是以字节为单位。
  • 对于[Type]数组,指针指向数组的第一个元素地址。

基础用法

Example01- 定义一个指针:

C :

int *a = malloc(sizeof(int)); *a = 42; printf("a‘s value: %d",*a); free(a) 

Swift :

let a = UnsafeMutablePointer<Int>.allocate(capacity: 1) a.pointee = 42 print("a‘s value: (a.pointee)") // 42 a.deallocate(capacity: 1) 

pointee可理解为解引(dereference),即用?*符号获得指针指向内存区域的值。

取址方式一致,使用?&获得变量的内存地址:

var a = 42 functionAcceptVariableAddress(&a) // 这里函数接受一个类型为 UnsafeMutablePointer<Int> 传参 

Example02 - 操作指针指向的值:

C :

int *a = malloc(sizeof(int)); *a = 42; *a += 100; // 先用 * 解引(dereference) 然后对指针指向的内存值做加法 

Swift :

var a = 42 withUnsafeMutablePointer(to: &a) { $0.pointee += 100 } // 要修改指针指向的内存值 所以用MutablePointer 

关键的两个指针操作函数withUnsafeMutablePointerwithUnsafePointer

/// Invokes `body` with an `UnsafeMutablePointer` to `arg` and returns the /// result. Useful for calling Objective-C APIs that take "in/out" /// parameters (and default-constructible "out" parameters) by pointer. public func withUnsafeMutablePointer<T,Result>(to arg: inout T,_ body: (UnsafeMutablePointer<T>) throws -> Result) rethrows -> Result /// Invokes `body` with an `UnsafePointer` to `arg` and returns the /// result. Useful for calling Objective-C APIs that take "in/out" /// parameters (and default-constructible "out" parameters) by pointer. public func withUnsafePointer<T,_ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result 

使用了泛型(generic) :T 和 Result。T 标示传入类型,即?a的类型?Int,由于标示了修饰关键字?inout,所以传入参数为指针(用&取址) ;?Body闭包的传参为?T类型指针,返回值类型为Result,推断可得还是?Int类型。闭包中对指针指向的内存区域进行直接操作,这里闭包中的$0为?arg的地址。


Example03 - 获取字符串中的字符 :

C :

char string[] = "hello"; printf("%cn",string[0]); // 输出‘h‘ 

Swift :

var string = "hello" // 5个字符 ‘h‘ ‘e‘ ‘l‘ ‘l‘ ‘o‘ 每个字符占一个字节 var strdata = string.data(using: .ascii) strdata?.withUnsafeBytes({ (ptr:UnsafePointer<Int8>) in print(ptr.pointee) // 104 = ‘h‘ }) 

Example04 - 自定义类型指针之间的转换:

C 以自定义结构体为例:

struct MyStruct { char name[6]; // 4个字节 int16_t reserved; // 保留 }; struct MyStruct1 { char firstname[2]; // 2个字节 姓 char lastname[4]; // 2个字节 名 int16_t reserved; // 保留 }; int main(int argc,const char * argv[]) { struct MyStruct myInfo = { .name = "machao",.reserved = 0x00000,}; printf("my name is %s n",myInfo.name); struct MyStruct1 *myInfo2 = (struct MyStruct1 *)&myInfo; printf("my lastname is %s",myInfo2->lastname); return 0; } 

Swift :

struct MyStruct {
    var name:(Int8,Int8,Int8) = (0x6d,0x61,0x63,0x68,0x6F)// 109(m) 97(a) 99(c) 104(h) 97(a) 111(o) var reserved:Int16 = 0x0000 // 保留 } struct MyStruct1 { var firstname:(Int8,Int8) // 2个字节 姓 var lastname:(Int8,Int8) // 4个字节 名 var reserved:Int16 // 保留 } var struct1 = MyStruct() withUnsafePointer(to: &struct1){ ptr in ptr.withMemoryRebound(to: MyStruct1.self,capacity: 1,{ ptrStruct1 in print("firstname (ptrStruct1.pointee.firstname)") print("lastname (ptrStruct1.pointee.lastname)") }) } 

to为要 cast 过去的Type,这里是MyStruct1capacity为整体容量,而非结构体总字节数;ptrStruct1为?&struct1指针转成?MyStruct类型后的指针,你可以直接通过它来?操作,可以看到我们用?pointee解引取到结构体,接着就可以随心所欲的操作里面的成员了。

Example05 - 将具有明确类型的指针转成?Void *指针:

C :

int *a = 10; void *ptr = (void *)a; // 注意一个强转就搞定了 

Swift :

let intPtr = UnsafeMutablePointer<Int>.allocate(capacity: 1) let voidPtr = UnsafeRawPointer(intPtr) let intPtrAgain = voidPtr.assumingMemoryBound(to: Int.self) 

assumingMemoryBound方法从字面理解即假设指针指向区域的内存大小,这里为Int.self,4个字节。

Example06 - 数组操作:

此处不给出 C 语言操作例程。

Swift:

var array = [1,2,3,4] var ptr = UnsafeBufferPointer(start: &array,count: 4) ptr.forEach { print("($0)") } 

实战

socket 聊天室

服务端代码:

import Foundation import Darwin /// ================= 客户端运行程序 ================= /// 创建 socket 接口 var sockfd_client:Int32 /// 用于bind服务端信息的 protofamily AN_INIT 决定ipv4(32位IP地址)和端口号(16位)的组合 var server_addr:sockaddr_in // 第一步:应对服务器的 socket 参数分别是:协议域(族) socket类型 协议 sockfd_client = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) // 第二步:填充服务器socket的信息 server_addr = sockaddr_in() server_addr.sin_family = sa_family_t(AF_INET) let _ = "127.0.0.1".data(using: .ascii)?.withUnsafeBytes({ (ptr: UnsafePointer<Int8>) in inet_pton(AF_INET,ptr,&server_addr.sin_addr.s_addr) }) server_addr.sin_port = 8000 // 第三步:连接服务器 自动分配一个端口 let _ = withUnsafePointer(to: &server_addr){ ptr in ptr.withMemoryRebound(to: sockaddr.self,capacity: 1) { ptrSockAddr in connect(sockfd_client,UnsafePointer(ptrSockAddr),socklen_t(MemoryLayout<sockaddr>.size)) } } var message = "12345678" let _ = message.data(using: .ascii)?.withUnsafeBytes({ (ptr:UnsafePointer<Int8>)in // 第四步: 向服务器发送一条信息 send(sockfd_client,message.characters.count,0) }) close(sockfd_client) 

客户端:

import Foundation import Darwin /// ================= 服务端运行程序 ================= /// 用于创建 listen 的 socket 接口 var socket_fd:Int32 /// 用于bind服务端信息的 protofamily AN_INIT 决定ipv4(32位IP地址)和端口号(16位)的组合 var server_addr:sockaddr_in // 第一步:创建一个 socket 参数分别是:协议域(族) socket类型 协议 socket_fd = socket(AF_INET,IPPROTO_TCP) // 第二步:创建一个 socket描述 填充具体的协议内容 包括端口号 IP server_addr = sockaddr_in() server_addr.sin_family = sa_family_t(AF_INET) server_addr.sin_addr.s_addr = INADDR_ANY // 设置IP 系统自动获取本机的IP地址 server_addr.sin_port = 8000 // 第三步:bind 这里需要将sockaddr_in * 强转成 sockaddr let _ = withUnsafePointer(to: &server_addr){ ptr in ptr.withMemoryRebound(to: sockaddr.self,capacity: 1) { ptrSockAddr in bind(socket_fd,socklen_t(MemoryLayout<sockaddr>.size)) } } // 第四步:开启监听 listen(socket_fd,10) print("?? 开启监听...") // 第五步:接受客户端发起的请求 var connect_fd: Int32 // 用于记录连接的客户端socket描述符 var connect_addrin: sockaddr = sockaddr()// 记录连接客户端的 ip port 等信息 var connect_length: socklen_t = 0// 记录socket地址的长度 var n : Int var buffer: [UInt8] = [UInt8](repeating: 0,count: 4096) repeat { connect_fd = accept(socket_fd,&connect_addrin,&connect_length) guard connect_fd != -1 else { continue } // 接受客户端过来的数据 n = recv(connect_fd,&buffer,4096,0) print("recv (buffer)") // 关闭连接 close(connect_fd); } while true 

文献

  • Using Swift with Cocoa and Objective-C (Swift 3)
  • Swift 3.0 Unsafe World
作者:NinthDay 链接:https://www.jianshu.com/p/8217bf3444a8 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读