Printing a variable memory address in swift
Is there anyway to simulate the [NSString stringWithFormat:@"%p", myVar]
, from Objective-C, in the new Swift language?
For example:
let str = "A String"
println(" str value \(str) has address: ?")
Solution 1:
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.
Solution 2:
Swift 2
This is now part of the standard library: unsafeAddressOf
.
/// Return an UnsafePointer to the storage used for `object`. There's
/// not much you can do with this other than use it to identify the
/// object
Swift 3
For Swift 3, use withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
Solution 3:
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
Solution 4:
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 😆.
Solution 5:
To get the (heap) address of an object
func address<T: AnyObject>(o: T) -> Int {
return unsafeBitCast(o, Int.self)
}
class Test {}
var o = Test()
println(NSString(format: "%p", address(o))) // -> 0x7fd5c8700970
(Edit: Swift 1.2 now includes a similar function called unsafeAddressOf
.)
In Objective-C this would be [NSString stringWithFormat:@"%p", o]
.
o
is a reference to the instance. So if o
is assigned to another variable o2
, the returned address for o2
will be the same.
This doesn't apply to structs (including String
) and primitive types (like Int
), because those live directly on the stack. But we can retrieve the location on the stack.
To get the (stack) address of a struct, build-in type or object reference
func address(o: UnsafePointer<Void>) -> Int {
return unsafeBitCast(o, Int.self)
}
println(NSString(format: "%p", address(&o))) // -> 0x10de02ce0
var s = "A String"
println(NSString(format: "%p", address(&s))) // -> 0x10de02ce8
var i = 55
println(NSString(format: "%p", address(&i))) // -> 0x10de02d00
In Objective-C this would be [NSString stringWithFormat:@"%p", &o]
or [NSString stringWithFormat:@"%p", &i]
.
s
is struct. So if s
is assigned to another variable s2
, the value will be copied and the returned address for s2
will be different.
How it fits together (pointer recap)
Like in Objective-C, there are two different addresses associated with o
. The first is the location of the object, the second is the location of the reference (or pointer) to the object.
Yes, this means that the content of address 0x7fff5fbfe658 is the number 0x6100000011d0 as the debugger can tell us:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
So, except for strings being structs, internally this all pretty much works the same as in (Objective-)C.
(Current as of Xcode 6.3)