快速打印可变内存地址


165

无论如何,是否可以[NSString stringWithFormat:@"%p", myVar]使用新的Swift语言从Objective-C 模拟?

例如:

let str = "A String"
println(" str value \(str) has address: ?")

2
在中[NSString stringWithFormat:@"%p", myVar]myVar必须是一个指针。在您的Swift代码中,str不是指针。因此,该比较不适用。
2014年

5
值得注意的是,至少当我键入此内容时,以上两个注释是不正确的。
Ben Leggiero

Answers:


112

迅捷2

现在,它已成为标准库的一部分: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

迅捷3

对于Swift 3,请使用withUnsafePointer

var str = "A String"
withUnsafePointer(to: &str) {
    print(" str value \(str) has address: \($0)")
}

2
unsafeAddressOf()仅适用于类类型(如@Nick在下面指出)。因此,只有在导入Foundation并将String桥接到NSString时,此答案才有效。在普通的Swift中,String是一个值类型,并且unsafeAddressOf不能用于获取其地址(这种尝试会导致编译错误)。
Stephen Schaub

29
如果我想用Swift 3 打印不可变值的地址怎么办?withUnsafePointer导致cannot pass immutable value as inout argument错误。
亚历山大·瓦森宁

12
即使它被接受并获得最高投票,它仍然给出错误的结果。示例:print(self.description)prints <MyViewController: 0x101c1d580>,我们将其用作参考。var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }打印0x16fde4028明显不同的地址。有人可以解释为什么吗?
亚历山大·瓦森宁

4
顺便说一句,0x101c1d580按预期打印:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
Alexander Vasenin

8
@nyg您的回答为我提供了导致实际错误原因的线索:UnsafePointer是一个结构,因此要打印它指向的地址(而不是结构本身),必须打印String(format: "%p", $0.pointee)
亚历山大·瓦塞宁

213

注意:这是参考类型。

斯威夫特4/5:

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

打印someVar的内存地址。(感谢@Ying)


Swift 3.1:

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

打印someVar的内存地址。



2
Xcode中8.2.1自动更正告诉我现在是print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
杰夫·

2
仅当someVar是一个对象时,这不是真的吗?如果someVar恰好是诸如struct之类的值类型,则每次执行时都会给出一个不同的地址。
OutOnAWeekend

3
@OutOnAWeekend您是对的,很可能是因为在作为参数传递时复制了该结构。使用Unmanaged它可以做到这样的:print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
nyg

1
随着斯威夫特4,我能得到这个咒语,打印以及- Unmanaged.passUnretained(someVar).toOpaque()(无需通用规格)

2
Swift 4的答案是完美的。如果您还想获取不打印的字符串表示形式,建议debugDescription在其末尾添加。
Patrick

51

请注意,这个答案很旧。它描述的许多方法不再起作用。特别.core是无法再访问。

但是@drew的答案是正确和简单的:

现在,它已成为标准库的一部分:unsafeAddressOf。

因此,您的问题的答案是:

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

这是被标记为正确的原始答案(出于后代/礼貌):

Swift会“隐藏”指针,但它们仍然存在。(因为运行时需要它,并且出于与Objc和C的兼容性原因)

但是,有几件事要了解,但是首先如何打印Swift String的内存地址?

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

这将打印字符串的内存地址,如果打开XCode-> Debug Workflow-> View Memory并转到打印的地址,您将看到字符串的原始数据。由于这是字符串文字,因此它是二进制存储区(不是堆栈或堆)内部的内存地址。

但是,如果您这样做

    var aString : String = "THIS IS A STRING" + "This is another String"
    NSLog("%p", aString.core._baseAddress)

    // example printed address 0x103f30020

这将在堆栈上,因为字符串是在运行时创建的

注意:.core._baseAddress没有记录,我在变量检查器中找到了它,以后可能会隐藏它

_baseAddress并非在所有类型上都可用,这里是带有CInt的另一个示例

    var testNumber : CInt = 289
    takesInt(&testNumber)

这样takesInt的C辅助函数在哪里

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

在Swift方面,此功能是 takesInt(intptr: CMutablePointer<CInt>),因此它将CMutablePointer带到CInt,您可以使用&varname来获取它

该函数打印0x7fff5fbfed98,并且在该内存地址上您将找到289(以十六进制表示)。您可以使用更改内容*intptr = 123456

现在,还有其他一些事情要知道。

迅速地,字符串是原始类型,而不是对象。
CInt是映射到C int类型的Swift类型。
如果要对象的内存地址,则必须做一些不同的事情。
Swift有一些与C交互时可以使用的Pointer Type
,您可以在这里阅读它们:Swift Pointer Types此外,您可以了解更多关于它们探索其声明的信息(cmd +单击该类型),以了解如何进行转换。一种指向另一种的指针

    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 是我通过此实现创建的C帮助程序函数

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

再次,显示一个打印地址的示例:0x6000000530b0,如果您通过内存检查器,则会找到您的NSString

您可以在Swift中使用指针做一件事(甚至可以使用inout参数来完成)

    func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>) 
    {
        stringa.memory = "String Updated";
    }

    var testString : NSString = "test string"
    println(testString)
    playWithPointer(&testString)
    println(testString)

或者,与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

是的,指针必须存在,但不仅因为您提到的兼容性原因。操作系统通常使用的指针。即使语言是高级语言,指针也必须在任何时候以任何语言存在于操作系统的引擎中。
Holex 2014年

我说“出于兼容性原因并且因为运行时需要它” :-)第二条语句概括了您在说的内容(我假设程序员知道并理解它,所以我花了几句话)
LombaX 2014年

我认为这不再适用了?
aleclarson 2014年

是。我从@Drew中编辑了正确的答案。
罗格

@LombaX我们只能打印引用类型的地址吗?我不能打印变量的地址吗?
AbhimanyuAryan 2015年

21

获取对象的(堆)地址

func address<T: AnyObject>(o: T) -> Int {
    return unsafeBitCast(o, Int.self)
}

class Test {}
var o = Test()
println(NSString(format: "%p", address(o))) // -> 0x7fd5c8700970

编辑: Swift 1.2现在包括一个名为的类似功能unsafeAddressOf。)

在Objective-C中,这将是[NSString stringWithFormat:@"%p", o]

o是对该实例的引用。因此,如果o将其分配给另一个变量o2,则返回的地址o2将相同。

这不适用于结构体(包括String)和基本类型(如Int),因为它们直接存在于堆栈中。但是我们可以检索堆栈上的位置。

获取结构,内置类型或对象引用的(堆栈)地址

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

在Objective-C中,该值为[NSString stringWithFormat:@"%p", &o][NSString stringWithFormat:@"%p", &i]

s是结构。因此,如果s将其分配给另一个变量s2,则将复制该值,并且的返回地址s2将不同。

它如何组合在一起(指针回顾)

像在Objective-C中一样,有两个不同的地址与关联o。第一个是对象的位置,第二个是对象的引用(或指针)的位置。

是的,这意味着地址0x7fff5fbfe658的内容是数字0x6100000011d0,调试器可以告诉我们:

(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0

因此,除了字符串是结构以外,在内部所有这些工作都与(Objective-)C中的工作相同。

(最新版本为Xcode 6.3)


嗯 获取对象属性的堆栈地址不一致。有任何想法吗?看看这个要点!
aleclarson 2014年

对象的属性在堆上,而不是堆栈上。当您将类实例的属性作为UnsafePointer传递时,Swift实际上首先复制该值,然后您获得副本的地址。我怀疑这是为了防止C代码绕过对象的接口并导致状态不一致。我不知道有没有解决办法。
nschum 2014年

这是我在Apple Developer Forums上创建一个线程。那里有一些不错的答复。
aleclarson 2014年

20

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
*/

要点


在Swift中,我们处理值类型(结构)或引用类型(类)。进行时:

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

在地址X处分配了一些内存,在该地址处我们将找到值42。这样做&n会创建一个指向地址X的指针,从而&n告诉我们n位置。

(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

进行时:

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

内存分配在两个位置:

  • 在类实例数据所在的地址Y处,以及
  • 在类实例引用所在的地址X处。

如前所述,类是引用类型:所以的值c位于地址X处,我们将在其中找到Y的值。在地址Y + 16处我们将找到foo,在地址Y + 24处我们将找到bar(在+ 0和+ 8处,我们将找到类型数据和引用计数,对此我不能告诉您太多...)。

(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是42(foo)和0x5484(bar)。

在这两种情况下,使用&n&c都会给我们提供地址X。对于值类型,这就是我们想要的,但不是引用类型。

进行时:

let referencePointer = UnsafeMutablePointer<C>(&c)

我们在引用上创建一个指针,即指向地址X的指针。使用时也是如此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)

现在我们对幕后情况有了更好的了解,并且现在我们在地址X上找到了地址Y(这是我们想要的地址),我们可以执行以下操作来获取地址:

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

验证中:

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

还有其他方法可以做到这一点:

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

toOpaque()实际打电话unsafeBitCast(c, to: UnsafeMutableRawPointer.self)

我希望这对我有帮助...。


只是注意到,尝试通过2个不同的实例打印相同结构的地址时MemoryLocation会产生2个不同的地址。
user1046037

@ user1046037谢谢,对类init进行了更改。我也得到两个不同的地址,但仅当我使用空结构时。使用空结构总是给我奇怪的结果。我的猜测是编译器进行了一些优化……
nyg

@ user1046037检查:pastebin.com/mpd3ujw2。显然,所有空结构都指向相同的内存地址。但是,当我们要将指针存储在变量中时,它将创建它(或结构的副本)的副本...
nyg

这很有趣,但是当打印两次时,它会打印不同的内存地址。上面的答案也是如此。
user1046037

@ user1046037我不确定我是否理解您的意思,您有一些代码吗?(我总是得到相同的内存地址)
nyg

15

参考类型:

  • 获取引用类型的内存地址是有意义的,因为它表示身份。
  • === 身份运算符用于检查2个对象指向相同的引用。
  • 使用ObjectIdentifier得到的内存地址

码:

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

值类型:

  • 获取值类型的内存地址的需求并不重要(因为它是一个值),因此重点将更多地放在值的相等性上。

12

只需使用此:

print(String(format: "%p", object))

如果您收到有关“ MyClass?”不符合CVarArg 的编译器投诉,则可以这样做extension Optional : CVarArg { }。否则,这似乎在打印地址时不会出现其他答案的所有“不安全”错误。
Devin Lane

它需要var _cVarArgEncoding: [Int]on的实现CVarArg。尚不清楚应如何实施。

10

如果您只想在调试器中看到它,而不对其进行任何其他操作,则无需实际获取Int指针。要获取内存中对象地址的字符串表示形式,只需使用如下代码:

public extension NSObject { // Extension syntax is cleaner for my use. If your needs stem outside NSObject, you may change the extension's target or place the logic in a global function
    public var pointerString: String {
        return String(format: "%p", self)
    }
}

用法示例:

print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...

另外,请记住,您可以简单地打印一个对象而不覆盖它的对象description,它会在更具描述性(如果经常是隐秘的)文本的旁边显示其指针地址。

print(self, "Doing something else...")
// Prints like: <MyModule.MyClass: 0x7fd190d0f270> Doing something else...
// Sometimes like: <_TtCC14__lldb_expr_668MyModule7MyClass: 0x7fd190d0f270> Doing something else...


1
简单而整洁!
badhanganesh

9

斯威夫特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)
    }
}

用法:

print("FileManager.default: \(String.pointer(FileManager.default))")
// FileManager.default: 0x00007fff5c287698

print("nil: \(String.pointer(nil))")
// nil: nil

这个答案是不正确的。如果您创建了两个相同类的实例,它将为两个实例返回相同的内存地址。 Unmanaged.passUnretained(myObject).toOpaque()而是可以正常工作。
Padraig's

@Padraig谢谢,我已经更新了您的代码。可悲的是它现在需要一个AnyObject参数。我希望将其Any作为输入类型。
neoneye

6

在关于数组的Swift4中:

    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)
    }

1
拯救了我的一天。应该有一个徽章“最近获得的最有用的信息”
Anton Tropashko,

确实,这是印制我的唯一方法self?.array
罗斯蒂斯拉夫·德鲁

4

其他答案也很好,尽管我正在寻找一种将指针地址获取为整数的方法:

let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)

/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int

只需一点跟进。


请参阅下面针对雨燕3.版本的答案
RenniePet

3

@Drew提供的答案只能用于类类型。
@nschum提供的答案只能用于结构类型。

但是,如果您使用第二种方法来获取具有值类型元素的数组的地址。Swift将复制整个数组,因为在Swift数组中,它是写时复制的,并且一旦将控制权交给C / C ++(通过使用&获取地址来触发),Swift就无法确保它具有这种行为。而且,如果您改用first方法,它将自动转换ArrayNSArray我们不想要的东西。

因此,我找到的最简单统一的方法是使用lldb指令frame variable -L yourVariableName

或者,您可以结合他们的答案:

func address(o: UnsafePointer<Void>) {
    let addr = unsafeBitCast(o, Int.self)
    print(NSString(format: "%p", addr))
}

func address<T: AnyObject>(o: T) -> String{
    let addr = unsafeBitCast(o, Int.self)
    return NSString(format: "%p", addr) as String
}

3

这是Swift 3的。

像@CharlieMonroe一样,我想以整数形式获取地址。具体来说,在没有线程名称可用的情况下,我希望在诊断日志记录模块中将Thread对象的地址用作线程ID。

根据Charlie Monroe的代码,这是我到目前为止提出的内容。但是请注意,我是Swift的新手,这可能不正确...

  // 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

最后一条语句是存在的,因为没有它,我将获得0x60000007DB3F之类的地址。最后一条语句中的模运算将其转换为0x7DB3F。


3

我在Swift 3上的解决方案

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

此代码创建类似默认描述的描述 <MyClass: 0x610000223340>


2

当然,这不是最快或最安全的方法。但这对我有用。这将允许任何nsobject子类采用此属性。

public extension NSObject {
    public var memoryAddress : String? {
        let str = "\(self.self)".components(separatedBy: ": ")
        guard str.count > 1 else { return nil }
        return str[1].replacingOccurrences(of: ">", with: "")            
    }
}

//usage 
let foo : String! = "hello"
Swift.print(foo.memoryAddress) // prints 0x100f12980

感谢您的反馈杰夫,我将更新答案
Charlton Provatas

1
没关系!这是我的错!您的代码可以与纯Swift类一起正常工作。对不起,这个错误。
杰夫,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.