Printing a variable memory address in swift

前端 未结 15 903
天涯浪人
天涯浪人 2020-11-28 01:16

Is there anyway to simulate the [NSString stringWithFormat:@\"%p\", myVar], from Objective-C, in the new Swift language?

For example:

le         


        
相关标签:
15条回答
  • 2020-11-28 01:45

    In Swift4 about Array:

        let array1 = [1,2,3]
        let array2 = array1
        array1.withUnsafeBufferPointer { (point) in
            print(point) // UnsafeBufferPointer(start: 0x00006000004681e0, count: 3)
        }
        array2.withUnsafeBufferPointer { (point) in
            print(point) // UnsafeBufferPointer(start: 0x00006000004681e0, count: 3)
        }
    
    0 讨论(0)
  • 2020-11-28 01:46

    TL;DR

    struct MemoryAddress<T>: CustomStringConvertible {
    
        let intValue: Int
    
        var description: String {
            let length = 2 + 2 * MemoryLayout<UnsafeRawPointer>.size
            return String(format: "%0\(length)p", intValue)
        }
    
        // for structures
        init(of structPointer: UnsafePointer<T>) {
            intValue = Int(bitPattern: structPointer)
        }
    }
    
    extension MemoryAddress where T: AnyObject {
    
        // for classes
        init(of classInstance: T) {
            intValue = unsafeBitCast(classInstance, to: Int.self)
            // or      Int(bitPattern: Unmanaged<T>.passUnretained(classInstance).toOpaque())
        }
    }
    
    /* Testing */
    
    class MyClass { let foo = 42 }
    var classInstance = MyClass()
    let classInstanceAddress = MemoryAddress(of: classInstance) // and not &classInstance
    print(String(format: "%018p", classInstanceAddress.intValue))
    print(classInstanceAddress)
    
    struct MyStruct { let foo = 1 } // using empty struct gives weird results (see comments)
    var structInstance = MyStruct()
    let structInstanceAddress = MemoryAddress(of: &structInstance)
    print(String(format: "%018p", structInstanceAddress.intValue))
    print(structInstanceAddress)
    
    /* output
    0x0000000101009b40
    0x0000000101009b40
    0x00000001005e3000
    0x00000001005e3000
    */
    

    (Gist)


    In Swift we deal either with value types (structures) or reference types (classes). When doing:

    let n = 42 // Int is a structure, i.e. value type
    

    Some memory is allocated at address X, and at this address we will find the value 42. Doing &n creates a pointer pointing to address X, therefore &n tells us where n is located.

    (lldb) frame variable -L n
    0x00000001005e2e08: (Int) n = 42
    (lldb) memory read -c 8 0x00000001005e2e08
    0x1005e2e08: 2a 00 00 00 00 00 00 00 // 0x2a is 42
    

    When doing:

    class C { var foo = 42, bar = 84 }
    var c = C()
    

    Memory is allocated in two places:

    • at address Y where the class instance data is located and
    • at address X where the class instance reference is located.

    As said, classes are reference types: so the value of c is located at address X, at which we'll find the value of Y. And at address Y + 16 we'll find foo and at address Y + 24 we'll find bar (at + 0 and + 8 we'll find type data and reference counts, I can't tell you much more about this...).

    (lldb) frame variable c // gives us address Y
    (testmem.C) c = 0x0000000101a08f90 (foo = 42, bar = 84)
    (lldb) memory read 0x0000000101a08f90 // reading memory at address Y
    0x101a08f90: e0 65 5b 00 01 00 00 00 02 00 00 00 00 00 00 00
    0x101a08fa0: 2a 00 00 00 00 00 00 00 54 00 00 00 00 00 00 00
    

    0x2a is 42 (foo) and 0x54 is 84 (bar).

    In both cases, using &n or &c will give us address X. For value types, that's what we want, but isn't for reference types.

    When doing:

    let referencePointer = UnsafeMutablePointer<C>(&c)
    

    We create a pointer on the reference, i.e. a pointer that points to address X. Same thing when using withUnsafePointer(&c) {}.

    (lldb) frame variable referencePointer
    (UnsafeMutablePointer<testmem.C>) referencePointer = 0x00000001005e2e00 // address X
    (lldb) memory read -c 8 0x00000001005e2e00 // read memory at address X
    0x1005e2e00: 20 ec 92 01 01 00 00 00 // contains address Y, consistent with result below:
    (lldb) frame variable c
    (testmem.C) c = 0x000000010192ec20 (foo = 42, bar = 84)
    

    Now that we have a better understanding of what goes on under the hood, and that we now that at address X we'll find address Y (which is the one we want) we can do the following to get it:

    let addressY = unsafeBitCast(c, to: Int.self)
    

    Verifying:

    (lldb) frame variable addressY -f hex
    (Int) addressY = 0x0000000101b2fd20
    (lldb) frame variable c
    (testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
    

    There are other ways to do this:

    let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
    let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
    

    toOpaque() actually calls unsafeBitCast(c, to: UnsafeMutableRawPointer.self).

    I hope this helped... it did for me

    0 讨论(0)
  • 2020-11-28 01:49

    Swift 5

    extension String {
        static func pointer(_ object: AnyObject?) -> String {
            guard let object = object else { return "nil" }
            let opaque: UnsafeMutableRawPointer = Unmanaged.passUnretained(object).toOpaque()
            return String(describing: opaque)
        }
    }
    

    Usage:

    print("FileManager.default: \(String.pointer(FileManager.default))")
    // FileManager.default: 0x00007fff5c287698
    
    print("nil: \(String.pointer(nil))")
    // nil: nil
    
    0 讨论(0)
提交回复
热议问题