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, Result>(to arg: inout 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("%c\n",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, Int8, Int8, Int8) = (0x6d, 0x61, 0x63,0x68, 0x61, 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, 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, ptr, 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, SOCK_STREAM, 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, UnsafePointer(ptrSockAddr), 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

文献

作者:NinthDay
链接:https://www.jianshu.com/p/8217bf3444a8
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Swift Pointer 使用指南的更多相关文章

  1. swift GCD使用指南

    swift GCD使用指南 Grand Central Dispatch(GCD)是异步执行任务的技术之一.一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需要定义想执行的任务并追加到适 ...

  2. Swift 编程风格指南(raywenderlich.com 版本号)

    官方 raywenderlich.com Swift 编程风格指南 本文版权归 raywenderlich.com .The Official raywenderlich.com Swift Styl ...

  3. Swift编程权威指南第2版 读后收获

    自从参加工作一直在用OC做iOS开发.在2015年的时候苹果刚推出swift1.0不久,当时毕竟是新推出的语言,大家也都很有激情的学习.不过在学完后发现很难在实际项目中使用,再加上当时公司项目都是基于 ...

  4. [转]Swift编程风格指南

    语言 使用美式英语拼写以匹配苹果公司的API 优选: var color = "red" 不建议使用: var colour = "red" 间隔 使用2个空格 ...

  5. raywenderlich.com的Swift编程风格指南

    翻译自:https://github.com/raywenderlich/swift-style-guide 这个风格指南可能和你从其它地方看到的不同,我们的焦点主要集中在互联网和文章上的可读性.创建 ...

  6. 一种swift编码风格指南(供参考,by linkedin)

    http://www.cocoachina.com/swift/20160701/16894.html

  7. Swift动画编程指南-01 简介

    大家好,我是老镇,这段时间家里和工作上发生了很多的事情,所以很长一段时间都没有出来搞什么小动作了.在接下来的一段时间内我会制作一些列关于使用Swift进行动画编程的视频,希望和大家胃口. 在iOS的世 ...

  8. Swift动画编程指南-02 Swift动画是怎么炼成的

    上一节我们看了几个很棒的例子,我们不禁会想.他们是怎么设计的,怎么从一个空白的画布变成一个完整的,美丽的动画.这些动画是如何产生的,是哪些属性被改变了.我们还要认真思考的是,每一个步骤到底发生了什么. ...

  9. 关于《Swift开发指南》背后的那些事

    时间轴(倒叙)2014年8月底在图灵出版社的大力支持下,全球第一本全面.系统.科学的,包含本人多年经验的呕心沥血之作<Swift开发指南>(配有同步视频课程和同步练习)全线重磅推出2014 ...

随机推荐

  1. Linux学习总结(3)——Linux实用工具

    1. Windows下同步Linux文件(Linux安装Samba和配置) 场景需求: 安装了Ubuntu在虚拟机上,但是代码编辑或者其它更多的操作的时候,还是习惯在windows下进行.如果wind ...

  2. poj 1273最大流dinic算法模板

    #include<stdio.h> #include<string.h> #define N 300 #define inf 0x7fffffff #include<qu ...

  3. Tomcat关闭后,重新启动,session中保存的对象为什么还存在解决方法

    Tomcat关闭后,重新启动,session中保存的对象为什么还存在各们朋友大家好:         当我关闭Tomcat,重新启动后,session中保存的对象还依然存在,仍然可以使用,不知这是什么 ...

  4. SQLServer到底支持多少连接数的并发?

    1)不启用连接池 当创建到101个连接的时候,就无法再创建新连接了.也就是说,如果连接字符串不做任何处理,我们的程序只能够跟SQLServer建立101个连接. 2)启用连接池 在连接字符串中加入代码 ...

  5. ubuntu设置不睡眠的方法

    Ubuntu系统不睡眠设置方法:要完成这个设置Ubuntu有两种方法:第一种是打开 System Settings –> Power,中文版是打开系统设置 -> 电源),然后进行设置.但不 ...

  6. Linux - Virsh

    virsh命令 suspend resume dumpxml   KVM平台以存储池的形式对存储进行统一管理,所谓存储池能够理解为本地文件夹.通过远端磁盘阵列(iSCSI.NFS)分配过来磁盘或文件夹 ...

  7. P​H​P​中​h​t​t​p​协​议​详​解

    对PHP文件来说 Php能够有  html   css javascript php脚本 flash它的不同部分是在不同的地方运行的(server和client) http协议 1. http协议是建 ...

  8. 欧几里得 &amp; 拓展欧几里得算法 解说 (Euclid &amp; Extend- Euclid Algorithm)

    欧几里得& 拓展欧几里得(Euclid & Extend-Euclid) 欧几里得算法(Euclid) 背景: 欧几里德算法又称辗转相除法.用于计算两个正整数a.b的最大公约数. -- ...

  9. spring mvc文件上传,request对象转换异常

    spring 文件上传有现成的工具用起来也挺简单.就是在还不是非常熟悉的时候可能会出一些错. 近期碰到了 org.apache.catalina.connector.RequestFacade can ...

  10. Instagram的Material Design概念设计文章分享

    近期開始研究最新的Android 5 Material Design,一加氢OS公布后,非常快就有一大批支持Android5原生风格的手机出来了,你的App还是UI帮设计的吗?该考虑升级到 Mater ...