2018-Read-Record 记录我的2018学习历程
Overview
C Syntax | Swift Syntax | Note |
---|---|---|
const Type * |
UnsafePointer |
指针可变,指针指向的内存值不可变。 |
Type * |
UnsafeMutablePointer |
指针和指针指向的内存值均可变。 |
ClassType * const * |
UnsafePointer |
指针的指针:指针不可变,指针指向的类可变。 |
ClassType * __strong * |
UnsafeMutablePointer |
指针的指针:指针和指针指向的类均可变。 |
ClassType ** |
AutoreleasingUnsafeMutablePointer |
|
const void * |
UnsafeRawPointer |
指针指向的内存区类型未定。 |
void * |
UnsafeMutableRawPointer |
同上 |
StructType * | OpaquePointer |
C 语言中的一些自定义类型,Swift 中并未有相对应的类型。 |
int8_t a[] |
var x:[Int8] -> UnsafeBufferPointer |
Buffer 一词不难联想到数组 |
对于声明为 UnsafePointer
作为参数的函数,同样可以接受如下类型:
-
UnsafePointer
,UnsafeMutablePointer
,AutoreleaseingUnsafeMutablePointer
, 以上类型可转成UnsafePointer
。 -
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.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 传参
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(to arg: inout T, _ body: (UnsafeMutablePointer) 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(to arg: inout T, _ body: (UnsafePointer) 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) 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.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) 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.size))
}
}
var message = "12345678"
let _ = message.data(using: .ascii)?.withUnsafeBytes({ (ptr:UnsafePointer)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.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