• Swift Pointer 使用指南


    Overview

    C SyntaxSwift SyntaxNote
    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
    ",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 
    ",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
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
  • 相关阅读:
    iOS:TabBarController 显示/隐藏第一级页面的TabBar
    iOS : Blur Effect
    ASIHTTPRequest / ASIFormDataRequest
    Xcode :Missing file warnings
    Axure设计软件下载安装及注册
    windows服务更改配置文件
    sql server 清理数据库日志
    sql server 随机生成布尔值
    sql server 授权相关命令
    用VS2015创建ASP.NET Web Forms 应用程序
  • 原文地址:https://www.cnblogs.com/feng9exe/p/9188197.html
Copyright © 2020-2023  润新知