[Swift基础语法入门] Swift Pointer 使用指南

2018-Read-Record 记录我的2018学习历程

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为 Int8Uint8 ,那么字符串将被转成 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 类型。闭包中对指针指向的内存区域进行直接操作,这里闭包中的$0arg 的地址。


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

文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容