在新的Swift语言中,是否有任何方式可以模拟Objective-C中的[NSString stringWithFormat:@"%p", myVar]
?
例如:
let str = "A String"
println(" str value \(str) has address: ?")
在新的Swift语言中,是否有任何方式可以模拟Objective-C中的[NSString stringWithFormat:@"%p", myVar]
?
例如:
let str = "A String"
println(" str value \(str) has address: ?")
注意:此处仅适用于参考类型。
print(Unmanaged.passUnretained(someVar).toOpaque())
打印someVar的内存地址。 (感谢@Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
打印someVar的内存地址。
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
。 - JeffUnmanaged
可以这样实现:print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
。 - nygUnmanaged.passUnretained(someVar).toOpaque()
(不需要泛型规范)。 - YingdebugDescription
。 - Patrick现在已经包含在标准库中: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
使用withUnsafePointer
来适用于 Swift 3:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
会导致cannot pass immutable value as inout argument
错误。 - Alexander Vaseninprint(self.description)
打印出<MyViewController: 0x101c1d580>
,我们将其用作参考。var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
打印出明显不同的地址0x16fde4028
。有人能解释一下为什么吗? - Alexander Vasenin0x101c1d580
:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
。 - Alexander VaseninUnsafePointer
是一个结构体,因此为了打印它所指向的地址(而不是结构体本身),你需要打印 String(format: "%p", $0.pointee)
! - Alexander Vasenin请注意,这个答案比较旧了,它描述的许多方法已经不再适用。特别是.core
无法再被访问。
然而@drew的回答是正确且简洁的:
现在这是标准库的一部分:unsafeAddressOf。
所以你的问题的答案是:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Swift“隐藏”指针,但在底层仍然存在。(因为运行时需要它,并且出于与Objc和C的兼容性考虑)
然而有几件事情需要知道,但首先如何打印Swift字符串的内存地址?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
如果你打开XCode -> 调试工作流程 -> 查看内存,并转到字符串的内存地址,就可以看到字符串的原始数据。由于这是一个字符串字面量,所以它是二进制存储空间内的一个内存地址(不是栈或堆)。
但是,如果你执行
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>)
,所以它接受一个指向CInt的CMutablePointer,你可以用&varname获得它。
该函数打印出0x7fff5fbfed98
,在这个内存地址中,你将找到289(十六进制表示)。你可以用*intptr = 123456
来更改它的内容。
现在,还有一些其他需要知道的事情。
在Swift中,String是一个原始类型,而不是一个对象。
CInt是映射到C int类型的Swift类型。
如果你想要一个对象的内存地址,你必须做一些不同的事情。
Swift有一些指针类型,在与C交互时可以使用,你可以在这里了解更多:Swift Pointer Types
此外,你可以通过探索它们的声明(cmd+click on the type)来理解如何将一种指针类型转换为另一种。
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
简述:
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()
内存分配在两个位置:
正如所说,类是引用类型:因此,变量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),0x54
是84(bar)。
在这两种情况下,使用&n
或&c
将给我们地址X。对于值类型来说,这正是我们想要的,但对于引用类型来说则不是。
执行以下操作时:
let referencePointer = UnsafeMutablePointer<C>(&c)
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)
。
希望这能帮到您... 对我来说很有帮助。
struct LinkedList<Value>
)中使用MemoryAddress
? - derpoliukextension 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()
可以正常工作。 - PadraigAnyObject
参数。我更喜欢将输入类型设为 Any
。 - neoneyefunc 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
[NSString stringWithFormat:@"%p", &o]
或[NSString stringWithFormat:@"%p", &i]
。
s
是结构体。因此,如果将s
分配给另一个变量s2
,则该值将被复制,并且s2
的返回地址将不同。
o
有两个不同的关联地址。第一个是对象的位置,第二个是对对象的引用(或指针)的位置。(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
===
身份运算符用于检查 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
只需使用此代码:
print(String(format: "%p", object))
MyClass?
" 不符合 CVarArg,你可以使用 extension Optional : CVarArg { }
。否则,这似乎会打印地址,而不会像其他答案那样带有所有的 "unsafe" 疯狂行为。 - Devin LaneCVarArg
上实现var _cVarArgEncoding: [Int]
。不清楚应该如何实现。 - Yuchen 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)
}
self?.array
的方法。 - Rostyslav Druzhchenko如果你只是想在调试器中查看它而不对其进行其他任何操作,那么实际上没有必要获取 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...
[NSString stringWithFormat:@"%p", myVar]
中,myVar
必须是一个指针。在您的 Swift 代码中,str
不是一个指针,因此该比较不适用。 - user102008