美文网首页
Swift指针

Swift指针

作者: BBLv | 来源:发表于2021-02-05 21:09 被阅读0次

    指针分类:

    • raw pointer:未指定数据类型的指针(原生指针)
    • typed pointer:指定数据类型的指针

    表示方式:

    raw pointer 在swift中的表示是UnsafeRawPointer

    typed pointer在swift中的表示是UnsafePointer<T>,是一个泛型

    Swift对照Objective-C,指针对应的关系:

    Swift Objective-C 说明
    UnsafePointer<T> const T * 指针和指向的内容都是不可变的
    UnsafeMutablePointer<T> T * 指针和指向的内容均是可变的
    UnsafeRawPointer const void * 指针指向未知的类型
    UnsafeMutableRawPointer void * 指针指向尾椎的类型(可以修改)
    原生指针的使用(RawPointer):
    //指针内存需要手动管理
    
    let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)
    
    for i in 0..<4 {
        //advanced:步长
        //storebytes:写入内存
        p.advanced(by: i * 8).storeBytes(of: i, as: Int.self)
    }
    
    for i in 0..<4 {
        let value = p.load(fromByteOffset: i * 8, as: Int.self)
        print("index\(i),value:\(value)")
    }
    
    p.deallocate()
    

    打印结果:

    index0,value:0
    index1,value:1
    index2,value:2
    index3,value:3
    Program ended with exit code: 0

    内容补充:在看Swift源码中查看UnsafeMutableRawPointer的过程中会有builtin

    //builtin(标准模块) -->在编译的过程中会匹配LLVM里面的类型和方法,在当前编译过程当中,减少编译时的内存负担

    创建类型指针:
    方式一:
    var age = 10
    let p = withUnsafePointer(to: &age) { $0 }
    
    方式二:
    let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    
    ptr.initialize(to: age)
    
    ptr.deinitialize(count: 1)
    
    ptr.deallocate()
    

    知识补充:单一表达式
    例子:

    //ptr in return ptr :单一表达式,可以直接使用ptr来表示,也可以直接使用$0来表示
    //方式一:
    { ptr in return ptr }
    方式二:
    { ptr }
    方式三:
    { $0 }
    
    创建泛型指针:
    struct LGTeacher {
        var age = 10
        var height = 1.85
    }
    
    var t = LGTeacher()
    
    let ptr = UnsafeMutablePointer<LGTeacher>.allocate(capacity: 2)
    
    ptr.initialize(to: LGTeacher())
    
    ptr.advanced(by: 1).initialize(to: LGTeacher(age: 20, height: 1.75))
    
    print(ptr[0])
    
    print(ptr[1])
    
    print(ptr.pointee)
    
    print((ptr + 1).pointee)
    
    print(ptr.successor().pointee)
    
    ptr.deinitialize(count: 2)
    
    ptr.deallocate()
    

    打印结果:

    LGTeacher(age: 10, height: 1.85)
    LGTeacher(age: 20, height: 1.75)
    LGTeacher(age: 10, height: 1.85)
    LGTeacher(age: 20, height: 1.75)
    LGTeacher(age: 20, height: 1.75)
    Program ended with exit code: 0
    

    知识补充:successor()

    print(ptr.successor().pointee) 与 print((ptr + 1).pointee) 结果是一致的,本质上successor()这个方法就是向前移动8字节

    实战一:将将变量t绑定到结构图HeapObject内存中

    思路:

    1、获取实例变量的内存地址(指针)
    2、RawPointer-->重新绑定到heapObject内存指针

    代码实现:

    struct HeapObject {
        var kind: UnsafeRawPointer
        var strongRef: UInt32
        var unownedRef: UInt32
    }
    
    class LGTeacher {
        var age = 18
    }
    
    var t = LGTeacher()
    
    //1、获取实例变量的内存地址(指针)
    let ptr = Unmanaged.passUnretained(t as AnyObject).toOpaque()
    
    //2、RawPointer-->重新绑定到heapObject内存指针
    let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1)
    
    print(heapObject.pointee)
    

    输出结果:

    HeapObject(kind: 0x0000000100008168, strongRef: 3, unownedRef: 0)
    Program ended with exit code: 0
    

    知识补充:

    1、Unmanaged:所有权的转换
    提供两个方法:
    passRetained(引用计数+1,获取指针)
    passUnretained(引用计数不+1,只获取指针)

    2、bindMemory:指针重定向

    实战二:将HeapObject中kind变量绑定到lg_swift_class

    思路:

    1、获取实例变量的内存地址(指针)
    2、将指针重新绑定到lg_swift_class类内存指针

    代码:

    struct HeapObject {
        var kind: UnsafeRawPointer
        var strongRef: UInt32
        var unownedRef: UInt32
    }
    
    struct lg_swift_class {
        var kind: UnsafeRawPointer
        var superClass: UnsafeRawPointer
        var cacheData1: UnsafeRawPointer
        var cacheData2: UnsafeRawPointer
        var data: UnsafeRawPointer
        var falgs: UInt32
        var instanceAddressOffset: UInt32
        var instanceSize: UInt32
        var finstanceAlignMaskags: UInt16
        var reserved: UInt16
        var classSize: UInt32
        var classAddressOffset: UInt32
        var description: UnsafeRawPointer
    }
    
    class LGTeacher {
        var age = 18
    }
    
    var t = LGTeacher()
    
    //1、获取实例变量的内存地址(指针)
    let ptr = Unmanaged.passUnretained(t as AnyObject).toOpaque()
    
    //2、RawPointer-->重新绑定到lg_swift_class内存指针
    let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1)
    
    let metaPtr = heapObject.pointee.kind.bindMemory(to: lg_swift_class.self, capacity: 1)
    
    print(metaPtr.pointee)
    

    输出结果:

    lg_swift_class(kind: 0x0000000100008140, superClass: 0x00007fff889888f8, cacheData1: 0x00007fff20206af0, cacheData2: 0x0000802000000000, data: 0x0000000100776562, falgs: 2, instanceAddressOffset: 0, instanceSize: 24, finstanceAlignMaskags: 7, reserved: 0, classSize: 136, classAddressOffset: 16, description: 0x0000000100003c3c)
    Program ended with exit code: 0
    

    说明:

    lg_swift_class(kind: 0x0000000100008140, superClass: 0x00007fff889888f8, cacheData1: 0x00007fff20206af0, cacheData2: 0x0000802000000000, data: 0x0000000100776562, falgs: 2, instanceAddressOffset: 0, instanceSize: 24, finstanceAlignMaskags: 7, reserved: 0, classSize: 136, classAddressOffset: 16, description: 0x0000000100003c3c)
    按照打印出来的结果来看,显示了具体内存地址的大小,与给定的数据类型相同

    扩展:xx类指针怎么转换成元类指针,使用bindMemory,原理同上

    实战三:assumingMemoryBound的使用:如果将元组tuple数据传递给testPointer方法

    思路:

    将tuple类型转换成UnsafePointer,然后使用assumingMemoryBound假定内存绑定,告诉编译器不要再次进行类型检查了

    代码:

    var tuple = (10, 20)
    
    func testPointer(_ p: UnsafePointer<Int>) {
        print(p)
        print("end")
    }
    
    //assumingMemoryBound:假定内存绑定,告诉编译器tulPtr已经绑定过Int类型了,现在tulptr就是Int类型,不需要再次进行编译检查了
    withUnsafePointer(to: &tuple) { (tulPtr: UnsafePointer<(Int, Int)>) in
        testPointer(UnsafeRawPointer(tulPtr).assumingMemoryBound(to: Int.self))
    }
    

    输出结果:

    0x0000000100008058
    (lldb) x/8g 0x0000000100008058
    0x100008058: 0x000000000000000a 0x0000000000000014
    0x100008068: 0x0000000000000000 0x0000000000000000
    0x100008078: 0x0000000000000000 0x0000000000000000
    0x100008088: 0x0000000000000000 0x0000000000000000
    (lldb) 
    

    根据格式化输出内存地址显示,tuple已经打印出来了,0xa是10,0x14是20

    实战四:assumingMemoryBound的使用:如何获取结构体类型的指针

    思路:

    通过原生指针+偏移量的方式

    代码:

    struct HeapObject {
        var strongRef = 10
        var unownedRef = 20
    }
    
    func testPointer(_ p: UnsafePointer<Int>) {
        print(p)
        print("end")
    }
    
    var t = HeapObject()
    
    withUnsafePointer(to: &t) { (ptr: UnsafePointer<HeapObject>) in
        //通过原生指针+内存偏移来获取
        let strongRefPtr = UnsafeRawPointer(ptr) + MemoryLayout<HeapObject>.offset(of: \HeapObject.strongRef)!
        testPointer(strongRefPtr.assumingMemoryBound(to: Int.self))
    }
    

    打印结果:

    0x0000000100008078
    (lldb) x/8g 0x0000000100008078
    0x100008078: 0x000000000000000a 0x0000000000000014
    0x100008088: 0x0000000100760680 0x0000000000000000
    0x100008098: 0x0000000000000000 0x0000000000000000
    0x1000080a8: 0x0000000000000000 0x0000000000000000
    (lldb) 
    

    根据格式或内存地址的结果说明:strongRef = 0xa =10,unownedRef = 0x14 = 20

    总结:学习过程中一点积累,越来越好,加油

    相关文章

      网友评论

          本文标题:Swift指针

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