Printing a variable memory address in swift

前端 未结 15 902
天涯浪人
天涯浪人 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:23

    Just use this:

    print(String(format: "%p", object))
    
    0 讨论(0)
  • 2020-11-28 01:25

    Reference Types:

    • It makes sense to get the memory address of a reference type as it represents identity.
    • === identity operator is used to check 2 objects point to the same reference.
    • Use ObjectIdentifier to get the memory address

    Code:

    class C {}
    
    let c1 = C()
    let c2 = c1
    
    //Option 1:
    print("c1 address: \(Unmanaged.passUnretained(c1).toOpaque())") 
    
    //Option 2:
    let o1 = ObjectIdentifier(c1)
    let o2 = ObjectIdentifier(c2)
    
    print("o1 -> c1 = \(o1)")
    print("o2 -> c2 = \(o2)")
    
    if o1 == o2 {
        print("c1 = c2")
    } else {
        print("c1 != c2")
    }
    
    //Output:
    //c1 address: 0x000060c000005b10
    //o1 -> c1 = ObjectIdentifier(0x000060c000005b10)
    //o2 -> c2 = ObjectIdentifier(0x000060c000005b10)
    //c1 = c2
    

    Value Types:

    • The need to get the memory address of a value type is not of much significance (as it is a value) and the emphasis would be more on the equality of the value.
    0 讨论(0)
  • 2020-11-28 01:26

    My solution on Swift 3

    extension MyClass: CustomStringConvertible {
        var description: String {
            return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
        }
    }
    

    this code create description like default description <MyClass: 0x610000223340>

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

    This is for Swift 3.

    Like @CharlieMonroe I wanted to get the address as an integer. Specifically, I wanted the address of a Thread object for use as a thread ID in a diagnostic logging module, for situations where no thread name was available.

    Based on Charlie Monroe's code, here's what I've come up with so far. But beware, I'm very new to Swift, this may not be correct ...

      // Convert the memory address of the current Thread object into an Int for use as a thread ID
      let objPtr = Unmanaged.passUnretained(Thread.current).toOpaque()
      let onePtr = UnsafeMutableRawPointer(bitPattern: 1)!  // 1 used instead of 0 to avoid crash
      let rawAddress : Int64 = onePtr.distance(to: objPtr) + 1  // This may include some high-order bits
      let address = rawAddress % (256 * 1024 * 1024 * 1024)  // Remove high-order bits
    

    The last statement is there because without it I was getting addresses like 0x60000007DB3F. The modulo operation in the last statement converts that into 0x7DB3F.

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

    Note: This is for reference types.

    Swift 4/5:

    print(Unmanaged.passUnretained(someVar).toOpaque())
    

    Prints the memory address of someVar. (thanks to @Ying)


    Swift 3.1:

    print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
    

    Prints the memory address of someVar.


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

    Note that this answer was quite old. Many of the methods it describes no longer work. Specifically .core cannot be accessed anymore.

    However @drew's answer is correct and simple:

    This is now part of the standard library: unsafeAddressOf.

    So the answer to your questions is:

    println(" str value \(str) has address: \(unsafeAddressOf(str))")
    

    Here is the original answer that was marked correct (for posterity/politeness):

    Swift "hides" pointers, but they still exists under the hood. (because the runtime needs it, and for compatibility reasons with Objc and C)

    There are few things to know however, but first how to print the memory address of a Swift String?

        var aString : String = "THIS IS A STRING"
        NSLog("%p", aString.core._baseAddress)  // _baseAddress is a COpaquePointer
       // example printed address 0x100006db0
    

    This prints the memory address of the string, if you open XCode -> Debug Workflow -> View Memory and go to the printed address, you will see the raw data of the string. Since this is a string literal, this is a memory address inside the storage of the binary (not stack or heap).

    However, if you do

        var aString : String = "THIS IS A STRING" + "This is another String"
        NSLog("%p", aString.core._baseAddress)
    
        // example printed address 0x103f30020
    

    This will be on the stack, because the string is created at runtime

    NOTE: .core._baseAddress is not documented, I found it looking in the variable inspector, and it may be hidden in the future

    _baseAddress is not available on all types, here another example with a CInt

        var testNumber : CInt = 289
        takesInt(&testNumber)
    

    Where takesInt is a C helper function like this

    void takesInt(int *intptr)
    {
        printf("%p", intptr);
    }
    

    On the Swift side, this function is takesInt(intptr: CMutablePointer<CInt>), so it takes a CMutablePointer to a CInt, and you can obtain it with &varname

    The function prints 0x7fff5fbfed98, an at this memory address you will find 289 (in hexadecimal notation). You can change its content with *intptr = 123456

    Now, some other things to know.

    String, in swift, is a primitive type, not an object.
    CInt is a Swift type mapped to the C int Type.
    If you want the memory address of an object, you have to do something different.
    Swift has some Pointer Types that can be used when interacting with C, and you can read about them here: Swift Pointer Types
    Moreover, you can understand more about them exploring their declaration (cmd+click on the type), to understand how to convert a type of pointer into another

        var aString : NSString = "This is a string"  // create an NSString
        var anUnmanaged = Unmanaged<NSString>.passUnretained(aString)   // take an unmanaged pointer
        var opaque : COpaquePointer = anUnmanaged.toOpaque()   // convert it to a COpaquePointer
        var mut : CMutablePointer = &opaque   // this is a CMutablePointer<COpaquePointer>
    
        printptr(mut)   // pass the pointer to an helper function written in C
    

    printptr is a C helper function I created, with this implementation

    void printptr(void ** ptr)
    {
        printf("%p", *ptr);
    }
    

    Again, an example of the address printed: 0x6000000530b0 , and if you go through memory inspector you will find your NSString

    One thing you can do with pointers in Swift (this can even be done with inout parameters)

        func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>) 
        {
            stringa.memory = "String Updated";
        }
    
        var testString : NSString = "test string"
        println(testString)
        playWithPointer(&testString)
        println(testString)
    

    Or, interacting with Objc / c

    // objc side
    + (void)writeString:(void **)var
    {
        NSMutableString *aString = [[NSMutableString alloc] initWithFormat:@"pippo %@", @"pluto"];
        *var = (void *)CFBridgingRetain(aString);   // Retain!
    }
    
    // swift side
    var opaque = COpaquePointer.null()   // create a new opaque pointer pointing to null
    TestClass.writeString(&opaque)
    var string = Unmanaged<NSString>.fromOpaque(opaque).takeRetainedValue()
    println(string)
    // this prints pippo pluto
    
    0 讨论(0)
提交回复
热议问题