Swift Pointer 使用指南
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
关键的两个指针操作函数withUnsafeMutablePointer和withUnsafePointer:
/// 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,这里是MyStruct1;capacity为整体容量,而非结构体总字节数;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 使用指南的更多相关文章
- swift GCD使用指南
swift GCD使用指南 Grand Central Dispatch(GCD)是异步执行任务的技术之一.一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需要定义想执行的任务并追加到适 ...
- Swift 编程风格指南(raywenderlich.com 版本号)
官方 raywenderlich.com Swift 编程风格指南 本文版权归 raywenderlich.com .The Official raywenderlich.com Swift Styl ...
- Swift编程权威指南第2版 读后收获
自从参加工作一直在用OC做iOS开发.在2015年的时候苹果刚推出swift1.0不久,当时毕竟是新推出的语言,大家也都很有激情的学习.不过在学完后发现很难在实际项目中使用,再加上当时公司项目都是基于 ...
- [转]Swift编程风格指南
语言 使用美式英语拼写以匹配苹果公司的API 优选: var color = "red" 不建议使用: var colour = "red" 间隔 使用2个空格 ...
- raywenderlich.com的Swift编程风格指南
翻译自:https://github.com/raywenderlich/swift-style-guide 这个风格指南可能和你从其它地方看到的不同,我们的焦点主要集中在互联网和文章上的可读性.创建 ...
- 一种swift编码风格指南(供参考,by linkedin)
http://www.cocoachina.com/swift/20160701/16894.html
- Swift动画编程指南-01 简介
大家好,我是老镇,这段时间家里和工作上发生了很多的事情,所以很长一段时间都没有出来搞什么小动作了.在接下来的一段时间内我会制作一些列关于使用Swift进行动画编程的视频,希望和大家胃口. 在iOS的世 ...
- Swift动画编程指南-02 Swift动画是怎么炼成的
上一节我们看了几个很棒的例子,我们不禁会想.他们是怎么设计的,怎么从一个空白的画布变成一个完整的,美丽的动画.这些动画是如何产生的,是哪些属性被改变了.我们还要认真思考的是,每一个步骤到底发生了什么. ...
- 关于《Swift开发指南》背后的那些事
时间轴(倒叙)2014年8月底在图灵出版社的大力支持下,全球第一本全面.系统.科学的,包含本人多年经验的呕心沥血之作<Swift开发指南>(配有同步视频课程和同步练习)全线重磅推出2014 ...
随机推荐
- SCU Travel
Travel The country frog lives in has n towns which are conveniently numbered by 1,2,…,n . Among n(n− ...
- broker监控dataguard配置
使用broker查看dataguard信息时有告警 DGMGRL> show configuration; Configuration - DRTEST Protection Mode: Max ...
- [bzoj3224]普通平衡树[Treap]
Treap 的各种操作,模板题,要再写几遍 #include <iostream> #include <algorithm> #include <cstdio> # ...
- C. Vladik and Memorable Trip DP
C. Vladik and Memorable Trip time limit per test 2 seconds memory limit per test 256 megabytes input ...
- Mac下OpenCV开发
1. 环境搭建 a) 安装Homebrew i. 下载地址:http://github.com/mxcl/homebrew/tarball/maste ...
- [RxJS] Get current value out of Subject (BehaviorSubject)
When you want to get the current value of a subject, you need to switch BehaviorSubject, it always e ...
- XMPP基本内容简单介绍
即时通讯技术简单介绍 即时通讯技术(IM)支持用户在线实时交谈.假设要发送一条信息,用户须要打开一个小窗体,以便让用户及其朋友在当中输入信息并让交谈两方都看到交谈的内容.有很多的IM系统,如AOL I ...
- 查询和删除数据表中反复数据的sql
1.查询表中反复数据. select * from people where peopleId in (select peopleId from people group by ...
- hdu 1010 Tempter of the Bone (奇偶性剪枝)
题意:有一副二维地图'S'为起点,'D'为终点,'.'是可以行走的,'X'是不能行走的.问能否只走T步从S走到D? 题解:最容易想到的就是DFS暴力搜索,,但是会超时...=_=... 所以,,要有其 ...
- SQL SEVER 2008中的演示样例数据库
SQL SEVER 2008数据库是什么我就不说了,我在这里分享一下怎样学习SQL SEVER 2008数据库,假设是对数据库或是SQL SEVER 数据库全然陌生或是不熟悉的人来说,建议看看一些视频 ...