อย่างไรก็ตามมีการจำลอง[NSString stringWithFormat:@"%p", myVar]
จาก Objective-C ในภาษา Swift ใหม่หรือไม่?
ตัวอย่างเช่น:
let str = "A String"
println(" str value \(str) has address: ?")
อย่างไรก็ตามมีการจำลอง[NSString stringWithFormat:@"%p", myVar]
จาก Objective-C ในภาษา Swift ใหม่หรือไม่?
ตัวอย่างเช่น:
let str = "A String"
println(" str value \(str) has address: ?")
คำตอบ:
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 ให้ใช้withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
ส่งผลให้เกิดcannot pass immutable value as inout argument
ข้อผิดพลาด
print(self.description)
พิมพ์<MyViewController: 0x101c1d580>
เราใช้เป็นข้อมูลอ้างอิง var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
พิมพ์0x16fde4028
ซึ่งเป็นที่อยู่ที่แตกต่างกันอย่างชัดเจน ใครช่วยอธิบายได้บ้าง
0x101c1d580
ตามที่คาดไว้:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
เป็น struct ดังนั้นเพื่อที่จะพิมพ์อยู่ก็ชี้ไป (และไม่ struct เอง) คุณจะต้องพิมพ์String(format: "%p", $0.pointee)
!
หมายเหตุ: ใช้สำหรับประเภทการอ้างอิง
print(Unmanaged.passUnretained(someVar).toOpaque())
พิมพ์ที่อยู่หน่วยความจำของ someVar (ขอบคุณ @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
พิมพ์ที่อยู่หน่วยความจำของ someVar
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
Unmanaged.passUnretained(someVar).toOpaque()
(ไม่จำเป็นต้องมีข้อกำหนดทั่วไป)
debugDescription
ไปยังจุดสิ้นสุดของมัน
โปรดทราบว่าคำตอบนี้ค่อนข้างเก่า หลายวิธีที่อธิบายไม่ทำงานอีกต่อไป โดยเฉพาะ.core
ไม่สามารถเข้าถึงได้อีกต่อไป
อย่างไรก็ตามคำตอบของ @ drew นั้นถูกต้องและเรียบง่าย:
นี่เป็นส่วนหนึ่งของไลบรารีมาตรฐาน: unsafeAddressOf
ดังนั้นคำตอบสำหรับคำถามของคุณคือ:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
นี่คือคำตอบเดิมที่ถูกทำเครื่องหมายถูกต้อง (สำหรับลูกหลาน / ความสุภาพ):
ตัวชี้ "ซ่อน" อย่างรวดเร็ว แต่ยังคงอยู่ภายใต้ฝากระโปรง (เพราะรันไทม์ต้องการมันและสำหรับเหตุผลด้านความเข้ากันได้กับ 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 -> ดูหน่วยความจำและไปที่ที่อยู่ที่พิมพ์คุณจะเห็นข้อมูลดิบของสตริง เนื่องจากนี่คือตัวอักษรสตริงนี่คือที่อยู่หน่วยความจำภายในที่เก็บข้อมูลของไบนารี (ไม่ใช่สแต็กหรือฮีป)
อย่างไรก็ตามถ้าคุณทำ
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);
}
ในด้านสวิฟท์ฟังก์ชั่นนี้takesInt(intptr: CMutablePointer<CInt>)
จึงใช้ CMutablePointer เป็น CInt และคุณสามารถรับมันด้วย & varname
ฟังก์ชั่นจะพิมพ์0x7fff5fbfed98
ที่อยู่หน่วยความจำนี้คุณจะพบ 289 (ในรูปแบบเลขฐานสิบหก) คุณสามารถเปลี่ยนเนื้อหาด้วย*intptr = 123456
ตอนนี้สิ่งอื่น ๆ ที่ต้องรู้
สตริงในสวิฟท์เป็นชนิดดั้งเดิมไม่ใช่วัตถุ
CInt เป็นประเภท Swift ที่จับคู่กับ C int Type
หากคุณต้องการที่อยู่หน่วยความจำของวัตถุคุณต้องทำสิ่งที่แตกต่าง
Swift มีประเภทตัวชี้บางอย่างที่สามารถใช้เมื่อโต้ตอบกับ C และคุณสามารถอ่านเกี่ยวกับพวกเขาได้ที่นี่: ประเภทตัวชี้ของ Swift
นอกจากนี้คุณสามารถเข้าใจเพิ่มเติมเกี่ยวกับพวกเขาสำรวจการประกาศ (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
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
จะเหมือนกัน
สิ่งนี้ใช้ไม่ได้กับ structs (รวมถึง 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
ในวัตถุประสงค์ -C นี้จะเป็น [NSString stringWithFormat:@"%p", &o]
[NSString stringWithFormat:@"%p", &i]
หรือ
s
คือ struct ดังนั้นหากs
กำหนดให้กับตัวแปรอื่นs2
ค่าจะถูกคัดลอกและที่อยู่ที่ส่งคืนสำหรับs2
จะแตกต่างกัน
เช่นเดียวกับใน Objective-C มีที่อยู่สองที่แตกต่างกันซึ่งเชื่อมโยงกับ o
มีสองที่อยู่ที่แตกต่างกันที่เกี่ยวข้องกับ ที่แรกก็คือตำแหน่งของวัตถุที่สองคือที่ตั้งของการอ้างอิง (หรือตัวชี้) ไปยังวัตถุ
ใช่นี่หมายความว่าเนื้อหาของที่อยู่ 0x7fff5fbfe658 เป็นหมายเลข 0x6100000011d0 เนื่องจากโปรแกรมดีบั๊กสามารถบอกเราได้:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
ดังนั้นยกเว้นสตริงที่มีโครงสร้างภายในทั้งหมดนี้ใช้งานได้ดีเหมือนใน (Objective-) C
(ปัจจุบัน ณ Xcode 6.3)
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()
มีการจัดสรรหน่วยความจำในสองแห่ง:
ดังที่กล่าวไว้คลาสเป็นประเภทอ้างอิง: ดังนั้นค่าของ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 (บาร์)
ในทั้งสองกรณีการใช้&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)
จริงเรียก
ฉันหวังว่าสิ่งนี้จะช่วย ... มันทำเพื่อฉัน😆
MemoryLocation
สร้างที่อยู่ 2 ที่แตกต่างกัน
===
ตัวดำเนินการเอกลักษณ์ใช้เพื่อตรวจสอบ 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 { }
คุณสามารถทำได้ มิฉะนั้นสิ่งนี้ดูเหมือนว่าจะพิมพ์ที่อยู่โดยไม่มีความวิกลจริต "ไม่ปลอดภัย" ของคำตอบอื่น ๆ ทั้งหมด
var _cVarArgEncoding: [Int]
CVarArg
ไม่ชัดเจนว่าควรนำไปปฏิบัติอย่างไร
หากคุณต้องการเห็นสิ่งนี้ในดีบักเกอร์และไม่ทำสิ่งใดกับมันไม่จำเป็นต้องได้รับพ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...
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()
ทำงานอย่างถูกต้องแทน
AnyObject
พารามิเตอร์ ฉันต้องการAny
เป็นประเภทอินพุต
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
แท้จริงนี้เป็นวิธีเดียวที่พิมพ์ของฉัน
คำตอบอื่น ๆ นั้นใช้ได้แม้ว่าฉันกำลังมองหาวิธีรับที่อยู่ตัวชี้เป็นจำนวนเต็ม:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
ติดตามเพียงเล็กน้อย
คำตอบ @Drew ให้สามารถใช้ได้กับประเภทชั้นเรียนเท่านั้น
คำตอบ @nschum ให้สามารถใช้ได้เฉพาะกับชนิดของโครงสร้าง
อย่างไรก็ตามถ้าคุณใช้วิธีที่สองเพื่อรับที่อยู่ของอาร์เรย์ที่มีองค์ประกอบประเภทค่า Swift จะคัดลอกอาร์เรย์ทั้งหมดเนื่องจากใน Swift array เป็น copy-on-write และ Swift ไม่สามารถตรวจสอบให้แน่ใจว่ามันทำงานแบบนี้เมื่อผ่านการควบคุมไปยัง C / C ++ (ซึ่งเป็นทริกเกอร์โดยใช้&
เพื่อรับที่อยู่) และถ้าคุณใช้วิธีแรกแทนมันจะแปลงArray
ไปNSArray
ซึ่งเป็นแน่นอนสิ่งที่เราไม่ต้องการ
frame variable -L yourVariableName
ดังนั้นการที่ง่ายและครบวงจรมากที่สุดวิธีที่ผมพบคือการใช้การเรียนการสอน lldb
หรือคุณสามารถรวมคำตอบของพวกเขา:
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
}
นี่สำหรับ Swift 3
เช่น @CharlieMonroe ฉันต้องการรับที่อยู่เป็นจำนวนเต็ม โดยเฉพาะฉันต้องการที่อยู่ของวัตถุเธรดเพื่อใช้เป็น 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 การดำเนินการ modulo ในคำสั่งสุดท้ายจะแปลงเป็น 0x7DB3F
วิธีแก้ปัญหาของฉันใน 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>
นี่ไม่ใช่วิธีที่เร็วหรือปลอดภัยที่สุดที่จะไป แต่มันใช้งานได้สำหรับฉัน สิ่งนี้จะยอมให้คลาสย่อยใด ๆ ของ 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
[NSString stringWithFormat:@"%p", myVar]
,myVar
ต้องชี้ ในรหัส Swift ของคุณstr
ไม่ใช่ตัวชี้ ดังนั้นการเปรียบเทียบจึงไม่มีผล