การพิมพ์ที่อยู่หน่วยความจำแบบแปรผันอย่างรวดเร็ว


166

อย่างไรก็ตามมีการจำลอง[NSString stringWithFormat:@"%p", myVar]จาก Objective-C ในภาษา Swift ใหม่หรือไม่?

ตัวอย่างเช่น:

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

2
ใน[NSString stringWithFormat:@"%p", myVar], myVarต้องชี้ ในรหัส Swift ของคุณstrไม่ใช่ตัวชี้ ดังนั้นการเปรียบเทียบจึงไม่มีผล
user102008

5
อย่างน้อยตอนที่ฉันพิมพ์สิ่งนี้ความคิดเห็นสองข้อข้างต้นไม่ถูกต้อง
Ben Leggiero

คำตอบ:


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 แบบธรรมดาสตริงเป็นชนิดค่าและ unsafeAddressOf ไม่สามารถใช้เพื่อรับที่อยู่ (ความพยายามส่งผลให้เกิดข้อผิดพลาดในการคอมไพล์)
Stephen Schaub

29
ถ้าฉันต้องการพิมพ์ที่อยู่ของค่าไม่เปลี่ยนรูปด้วย Swift 3 withUnsafePointerส่งผลให้เกิดcannot pass immutable value as inout argumentข้อผิดพลาด
Alexander Vasenin

12
แม้ว่าจะได้รับการยอมรับและได้รับคำตอบสูงสุด แต่ก็ยังให้ผลลัพธ์ที่ผิด ตัวอย่าง: print(self.description)พิมพ์<MyViewController: 0x101c1d580>เราใช้เป็นข้อมูลอ้างอิง var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }พิมพ์0x16fde4028ซึ่งเป็นที่อยู่ที่แตกต่างกันอย่างชัดเจน ใครช่วยอธิบายได้บ้าง
Alexander Vasenin

4
BTW พิมพ์นี้0x101c1d580ตามที่คาดไว้:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
Alexander Vasenin

8
@nyg คำตอบของคุณให้ฉันเบาะแสสำหรับสาเหตุข้อผิดพลาดที่เกิดขึ้นจริง: UnsafePointerเป็น struct ดังนั้นเพื่อที่จะพิมพ์อยู่ก็ชี้ไป (และไม่ struct เอง) คุณจะต้องพิมพ์String(format: "%p", $0.pointee)!
Alexander Vasenin

214

หมายเหตุ: ใช้สำหรับประเภทการอ้างอิง

สวิฟท์ 4/5:

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

พิมพ์ที่อยู่หน่วยความจำของ someVar (ขอบคุณ @Ying)


สวิฟท์ 3.1:

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

พิมพ์ที่อยู่หน่วยความจำของ someVar



2
Xcode 8.2.1 การแก้ไขอัตโนมัติกำลังบอกฉันตอนนี้เลยprint(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Jeff

2
สิ่งนี้จะไม่เป็นจริงถ้า someVar นั้นเป็นวัตถุ หาก someVar เกิดขึ้นเป็นประเภทค่าเช่น struct สิ่งนี้จะให้ที่อยู่ที่แตกต่างกันทุกครั้งที่มีการดำเนินการ
OutOnAWeekend

3
@OutOnAWeekend คุณถูกต้องส่วนใหญ่อาจเป็นเพราะโครงสร้างถูกคัดลอกเมื่อส่งผ่านเป็นอาร์กิวเมนต์ การใช้ก็สามารถทำได้เช่นนี้Unmanaged print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
nyg

1
ในฐานะของ Swift 4 ฉันสามารถได้รับคาถานี้เพื่อพิมพ์เช่นกัน - Unmanaged.passUnretained(someVar).toOpaque()(ไม่จำเป็นต้องมีข้อกำหนดทั่วไป)
Ying

2
คำตอบ Swift 4 นั้นสมบูรณ์แบบ หากคุณต้องการได้รับการแสดงสตริงโดยไม่ต้องพิมพ์ฉันขอแนะนำให้เพิ่มdebugDescriptionไปยังจุดสิ้นสุดของมัน
แพทริค

51

โปรดทราบว่าคำตอบนี้ค่อนข้างเก่า หลายวิธีที่อธิบายไม่ทำงานอีกต่อไป โดยเฉพาะ.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

ใช่ตัวชี้ต้อง exsit แต่ไม่เพียงสำหรับเหตุผลความเข้ากันได้ตามที่คุณอ้างอิง ตัวชี้มักจะใช้โดยระบบปฏิบัติการ แม้ว่าภาษานั้นเป็นภาษาระดับสูงตัวชี้ต้องมีอยู่ในภาษาใด ๆ ในเวลาใดก็ได้ในเอ็นจินของระบบปฏิบัติการ
holex

ผมบอกว่า "สำหรับเหตุผลที่เข้ากันได้และเพราะรันไทม์ต้องการมัน" :-) คำสั่งที่สองนิยายที่สิ่งที่คุณพูด (ฉันคิดว่าเป็นโปรแกรมเมอร์ที่รู้และเข้าใจมันดังนั้นผมใช้คำไม่กี่คำ)
LombaX

ฉันถือว่านี่ใช้ไม่ได้อีกต่อไปหรือ
aleclarson

ใช่. ฉันแก้ไขคำตอบที่ถูกต้องจาก @Drew เข้าไป
Rog

@LombaX เราสามารถพิมพ์ที่อยู่ของประเภทการอ้างอิงได้หรือไม่ ฉันไม่สามารถพิมพ์ที่อยู่ของตัวแปรได้หรือไม่
AbhimanyuAryan

21

เพื่อรับที่อยู่ (heap) ของวัตถุ

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) เพราะสิ่งเหล่านั้นอาศัยอยู่บนสแต็กโดยตรง แต่เราสามารถเรียกคืนตำแหน่งบนสแต็กได้

ในการรับที่อยู่ (สแต็ค) ของ struct ให้สร้างชนิดในตัวหรืออ้างอิงวัตถุ

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)


อืมมม การรับที่อยู่สแต็กของคุณสมบัติของวัตถุนั้นไม่สอดคล้องกัน ความคิดใด ๆ ตรวจสอบส่วนสำคัญนี้!
aleclarson

คุณสมบัติของวัตถุอยู่ในฮีปไม่ใช่สแต็ก เมื่อคุณผ่านคุณสมบัติของอินสแตนซ์ของคลาสเป็น UnsafePointer Swift จะคัดลอกค่าก่อนและคุณจะได้รับที่อยู่ของสำเนา ฉันสงสัยว่าจะป้องกันรหัส C จากการหลีกเลี่ยงส่วนต่อประสานของวัตถุและทำให้เกิดสถานะไม่สอดคล้องกัน ฉันไม่รู้ว่ามีวิธีแก้ไขไหม
nschum

นี่คือฉันด้ายสร้างขึ้นในแอปเปิ้ลฟอรั่มสำหรับนักพัฒนา มีคำตอบที่ดีในนั้น
aleclarson

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 ซึ่งเป็นที่ตั้งของข้อมูลอินสแตนซ์ของชั้นเรียนและ
  • at address 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) และ 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)จริงเรียก

ฉันหวังว่าสิ่งนี้จะช่วย ... มันทำเพื่อฉัน😆


เพิ่งสังเกตเห็นว่าในขณะที่พยายามพิมพ์ที่อยู่ของ struct เดียวกันผ่าน 2 อินสแตนซ์ที่ต่างกันของการMemoryLocationสร้างที่อยู่ 2 ที่แตกต่างกัน
user1046037

@ user1046037 ขอขอบคุณทำการเปลี่ยนแปลงสำหรับคลาส init ฉันยังได้รับสองที่อยู่ที่แตกต่างกัน แต่เมื่อฉันใช้โครงสร้างที่ว่างเปล่า การใช้ struct เปล่าจะให้ผลลัพธ์แปลก ๆ แก่ฉันเสมอ ฉันเดาว่าคอมไพเลอร์ทำให้การเพิ่มประสิทธิภาพบางอย่าง ...
nyg

@ user1046037 ตรวจสอบ: pastebin.com/mpd3ujw2 เห็นได้ชัดว่าโครงสร้างที่ว่างเปล่าทั้งหมดชี้ไปยังที่อยู่หน่วยความจำเดียวกัน อย่างไรก็ตามเมื่อเราต้องการเก็บตัวชี้ไว้ในตัวแปรมันจะสร้างสำเนาของมัน (หรือของ struct?) ...
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] CVarArgไม่ชัดเจนว่าควรนำไปปฏิบัติอย่างไร
Yuchen Zhong

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
โปรดมาพร้อมกับ downvotes ใด ๆ ที่มีความคิดเห็นที่อธิบายว่าทำไมดังนั้นผมและคนอื่น ๆ มาที่นี่รู้ว่าทำไมนี้เป็นวิธีการแก้ปัญหาที่ไม่ดี :)
เบน Leggiero

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

@Padraig ขอบคุณฉันมีการปรับปรุงด้วยรหัสของคุณ น่าเสียดายที่ตอนนี้รับAnyObjectพารามิเตอร์ ฉันต้องการAnyเป็นประเภทอินพุต
neoneye

6

ใน Swift4 เกี่ยวกับ Array:

    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แท้จริงนี้เป็นวิธีเดียวที่พิมพ์ของฉัน
Rostyslav Druzhchenko

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

ติดตามเพียงเล็กน้อย


ดูด้านล่างสำหรับรุ่นของคำตอบสำหรับ Swift 3
RenniePet

3

คำตอบ @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
}

3

นี่สำหรับ 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


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 ล้วนๆ ขออภัยในความผิดพลาด
Jeff
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.