美文网首页
[Swift5.1] 21-指针

[Swift5.1] 21-指针

作者: codeTao | 来源:发表于2020-06-06 16:08 被阅读0次

    指针

    Swift中也有专门的指针类型,这些都被定性为“Unsafe”(不安全的),常见的有以下4种类型:

    • UnsafePointer<Pointee> 类似于 const Pointee *
    • UnsafeMutablePointer<Pointee> 类似于 Pointee *
    • UnsafeRawPointer 类似于 const void *
    • UnsafeMutableRawPointer 类似于 void *

    UnsafePointerUnsafeMutablePointer指针可以通过pointee属性获取指针指向内存地址的值, 并访问或者修改该值.

    var age = 10
    func test1(_ ptr: UnsafeMutablePointer<Int>) {  // int *
        ptr.pointee += 10
    }
    func test2(_ ptr: UnsafePointer<Int>) {  // const int *
        print(ptr.pointee)
    }
    test1(&age) 
    test2(&age) // 20
    print(age) // 20
    
    • UnsafeMutableRawPointer指针通过storeBytes(of: as:) 存储数据.
    • UnsafeRawPointer指针通过load(as:)访问数据.
    var age = 10
    func test3(_ ptr: UnsafeMutableRawPointer) { // void *
        ptr.storeBytes(of: 20, as: Int.self)
    }
    func test4(_ ptr: UnsafeRawPointer) { // const void *
        print(ptr.load(as: Int.self))
    }
    test3(&age)  
    test4(&age) // 20
    print(age) // 20
    

    指针的应用示例

    var arr = NSArray(objects: 11, 22, 33, 44)
    arr.enumerateObjects { (obj, idx, stop) in
        print(idx, obj)
        if idx == 2 { // 下标为2就停止遍历
            stop.pointee = true
        }
    }
    

    swift推荐遍历使用enumerated, 从(idx, obj) 元组中获取索引和值

    var arr = NSArray(objects: 11, 22, 33, 44)
    for (idx, obj) in arr.enumerated() {
        print(idx, obj)  
        if idx == 2 {
            break
        }
    }
    

    获得指向某个变量的指针

    • withUnsafeMutablePointer(to:) { $0 }
    • withUnsafePointer(to:) { $0 }
    var age = 11
    var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
    var ptr2 = withUnsafePointer(to: &age) { $0 }
    ptr1.pointee = 22
    print(ptr2.pointee) // 22
    print(age) // 22
    
    var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer($0) }
    var ptr4 = withUnsafePointer(to: &age) { UnsafeRawPointer($0) }
    ptr3.storeBytes(of: 33, as: Int.self)
    print(ptr4.load(as: Int.self)) // 33
    print(age) // 33
    

    获得指向堆空间实例的指针

    class Person {}
    var person = Person()
    var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
    var heapPtr = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))  
    //ptr.load(as: UInt.self) 堆空间person对象地址值
    print(heapPtr!)  // 0x000000010055b5d0
    
    • ptr指向person指针变量,等价于person本身,存储person地址值
    • bitPattern: 传入内存地址, heapPtr存储传入的地址
    var age = 11  //值类型
    var ptr2 = withUnsafePointer(to: &age) { $0 }
    //ptr2.pointee == age
    print(ptr2)  // 0x00000001000031b0
    

    创建指针

    1)指定内存创建指针

    var ptr = UnsafeRawPointer(bitPattern: 0x100001234)
    

    2)malloc方式创建指针

    // 创建
    var ptr = malloc(16)  // 堆空间分配16个字节
    // 存
    ptr?.storeBytes(of: 11, as: Int.self)
    ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self) 
    // toByteOffset 偏移量, 从ptr开始偏移8个字节,后8个字节存储Int类型 22 
    
    // 取
    print((ptr?.load(as: Int.self))!) // 11
    print((ptr?.load(fromByteOffset: 8, as: Int.self))!) // 22
    // 销毁
    free(ptr)
    

    3)allocate方式创建指针

    • 注意:只有UnsafeMutableRawPointerUnsafeMutablePointer 才可以调用 allocate分配内存.
    • UnsafeMutableRawPointer调用allocate
    var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
    // 存储
    ptr.storeBytes(of: 11, as: Int.self) 
    // 指针偏移8个字节,存储Int类型 22 
    ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)
    // 取
    print(ptr.load(as: Int.self)) // 11
    print(ptr.advanced(by: 8).load(as: Int.self)) // 22
    //释放
    ptr.deallocate()
    
    • UnsafeMutablePointer 调用allocate
      泛型指针,建议使用initialize初始化指针
      注意:该方式一定要调用deinitialize(count:)deallocate ()销毁对象,否则产生内存泄漏. 反初始化deinitialize(count:)allocate(capacity:)对应.
    //创建
    //有泛型Int , 只需要写容量即可. capacity 表示申请 3* 8 = 24个字节
    var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)
    //存储
    ptr.initialize(to: 11)    //初始化前8个字节
    ptr.successor().initialize(to: 22) //下一个Int 初始化22
    ptr.successor().successor().initialize(to: 33)
    
    //取值方式一:
    print(ptr.pointee) // 11
    print((ptr + 1).pointee) //22
    print((ptr + 2).pointee) //33
    //取值方式二:
    print(ptr[0]) //11
    print(ptr[1]) //22
    print(ptr[2]) //33
    //取值方式三 等价于 方式一和二
    print(ptr.pointee)
    print(ptr.successor().pointee)
    print(ptr.successor().successor().pointee)
    
    //销毁
    ptr.deinitialize(count: 3) 
    //反初始化, deinitialize(count:)和allocate(capacity:) 对应
    ptr.deallocate()
    
    • 示例: 创建3个指针指向的Person对象
    class Person {
        var age: Int
        var name: String
        init(age: Int, name: String) {
            self.age = age
            self.name = name
        }
        deinit { print(name, "deinit") }
    }
    
    var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
    ptr.initialize(to: Person(age: 10, name: "Jack"))
    (ptr + 1).initialize(to: Person(age: 11, name: "Rose"))
    (ptr + 2).initialize(to: Person(age: 12, name: "Kate"))
    // Jack deinit
    // Rose deinit
    // Kate deinit
    ptr.deinitialize(count: 3)
    ptr.deallocate()
    

    指针之间的转换

    • assumingMemoryBound(to:) 可以将RawPointer 非泛型指针转为 泛型指针
    //创建非泛型指针
    var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
    
    ptr.assumingMemoryBound(to: Int.self).pointee = 11
    //非泛型指针 + 8 , 就是偏移8个字节
    (ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0
    
    print(unsafeBitCast(ptr, to: UnsafePointer<Int>.self).pointee) // 11
    print(unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self).pointee) // 22.0
    
    ptr.deallocate()
    
    • unsafeBitCast原理: unsafeBitCast是忽略数据类型的强制转换,不会因为数据类型的变化而改变原来的内存数据.(简单说: 直接将二进制数据搬过去, 只是类型发生改变)
    • 类似于C++中的reinterpret_cast
    class Person {}
    var person = Person()
    var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self)
    print(ptr)
    

    相关文章

      网友评论

          本文标题:[Swift5.1] 21-指针

          本文链接:https://www.haomeiwen.com/subject/cvggzhtx.html