Apakah ada cara untuk mensimulasikan [NSString stringWithFormat:@"%p", myVar]
, dari Objective-C, dalam bahasa Swift yang baru?
Sebagai contoh:
let str = "A String"
println(" str value \(str) has address: ?")
Apakah ada cara untuk mensimulasikan [NSString stringWithFormat:@"%p", myVar]
, dari Objective-C, dalam bahasa Swift yang baru?
Sebagai contoh:
let str = "A String"
println(" str value \(str) has address: ?")
Jawaban:
Ini sekarang merupakan bagian dari perpustakaan standar: 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
Untuk Swift 3, gunakan withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
mengakibatkan cannot pass immutable value as inout argument
kesalahan.
print(self.description)
cetak <MyViewController: 0x101c1d580>
, kami menggunakannya sebagai referensi. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
cetakan 0x16fde4028
yang alamatnya jelas berbeda. Adakah yang bisa menjelaskan mengapa?
0x101c1d580
seperti yang diharapkan:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
adalah sebuah struct, jadi untuk mencetak alamat yang ditunjuknya (dan bukan struct itu sendiri) Anda harus mencetak String(format: "%p", $0.pointee)
!
Catatan: Ini untuk jenis referensi.
print(Unmanaged.passUnretained(someVar).toOpaque())
Mencetak alamat memori someVar. (terima kasih kepada @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Mencetak alamat memori someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
itu dapat dilakukan seperti ini: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(tidak perlu spesifikasi umum)
debugDescription
untuk menambahkannya .
Perhatikan bahwa jawaban ini sudah cukup lama. Banyak metode yang dijelaskannya tidak lagi berfungsi. Khususnya .core
tidak dapat diakses lagi.
Namun, jawaban @ drew benar dan sederhana:
Ini sekarang bagian dari perpustakaan standar: unsafeAddressOf.
Jadi jawaban untuk pertanyaan Anda adalah:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Inilah jawaban asli yang ditandai benar (untuk keturunan / kesopanan):
Swift "menyembunyikan" pointer, tetapi mereka masih ada di bawah tenda. (karena runtime membutuhkannya, dan untuk alasan kompatibilitas dengan Objc dan C)
Ada beberapa hal yang perlu diketahui, tetapi pertama-tama bagaimana cara mencetak alamat memori dari Swift String?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Ini mencetak alamat memori dari string, jika Anda membuka XCode -> Debug Workflow -> View Memory dan pergi ke alamat yang dicetak, Anda akan melihat data mentah dari string. Karena ini adalah string literal, ini adalah alamat memori di dalam penyimpanan biner (bukan stack atau heap).
Namun, jika Anda melakukannya
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Ini akan berada di tumpukan, karena string dibuat saat runtime
CATATAN: .core._baseAddress tidak didokumentasikan, saya menemukannya mencari di variabel inspector, dan mungkin disembunyikan di masa depan
_baseAddress tidak tersedia di semua jenis, di sini contoh lain dengan CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Di mana takesInt
fungsi pembantu C seperti ini
void takesInt(int *intptr)
{
printf("%p", intptr);
}
Di sisi Swift, fungsi ini adalah takesInt(intptr: CMutablePointer<CInt>)
, jadi dibutuhkan CMutablePointer ke CInt, dan Anda bisa mendapatkannya dengan & varname
Fungsi mencetak 0x7fff5fbfed98
, pada alamat memori ini Anda akan menemukan 289 (dalam notasi heksadesimal). Anda dapat mengubah kontennya dengan*intptr = 123456
Sekarang, beberapa hal lain yang perlu diketahui.
String, dengan cepat, adalah tipe primitif, bukan objek.
CInt adalah tipe Swift yang dipetakan ke Tipe C int.
Jika Anda ingin alamat memori suatu objek, Anda harus melakukan sesuatu yang berbeda.
Swift memiliki beberapa Tipe Pointer yang dapat digunakan saat berinteraksi dengan C, dan Anda dapat membacanya di sini: Tipe Swift Pointer
Selain itu, Anda dapat memahami lebih lanjut tentang mereka yang mengeksplorasi deklarasi mereka (cmd + klik pada jenisnya), untuk memahami cara mengonversi jenis pointer ke yang lain
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
adalah fungsi pembantu C yang saya buat, dengan implementasi ini
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Sekali lagi, contoh alamat dicetak:, 0x6000000530b0
dan jika Anda pergi melalui inspektur memori Anda akan menemukan NSString Anda
Satu hal yang dapat Anda lakukan dengan pointer di Swift (ini bahkan dapat dilakukan dengan parameter inout)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Atau, berinteraksi dengan 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
( Sunting: Swift 1.2 sekarang termasuk fungsi serupa yang disebut unsafeAddressOf
.)
Dalam Objective-C ini akan menjadi [NSString stringWithFormat:@"%p", o]
.
o
adalah referensi ke instance. Jadi, jika o
ditugaskan ke variabel lain o2
, alamat yang dikembalikan untuk o2
akan sama.
Ini tidak berlaku untuk struct (termasuk String
) dan tipe primitif (seperti Int
), karena mereka tinggal langsung di stack. Tetapi kita dapat mengambil lokasi di stack.
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
Dalam Objective-C ini akan menjadi [NSString stringWithFormat:@"%p", &o]
atau [NSString stringWithFormat:@"%p", &i]
.
s
adalah struct. Jadi jika s
ditugaskan ke variabel lain s2
, nilainya akan disalin dan alamat yang dikembalikan s2
akan berbeda.
Seperti di Objective-C, ada dua alamat berbeda yang terkait dengannya o
. Yang pertama adalah lokasi objek, yang kedua adalah lokasi referensi (atau pointer) ke objek.
Ya, ini berarti bahwa isi alamat 0x7fff5fbfe658 adalah angka 0x610000001111 seperti yang dapat dikatakan oleh debugger kepada kami:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Jadi, kecuali untuk string yang menjadi struct, secara internal semua ini cukup banyak bekerja sama dengan di (Objective-) C.
(Lancar pada 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
*/
( Intisari )
Di Swift kami berurusan dengan tipe nilai (struktur) atau tipe referensi (kelas). Ketika melakukan:
let n = 42 // Int is a structure, i.e. value type
Beberapa memori dialokasikan pada alamat X, dan pada alamat ini kita akan menemukan nilai 42. Melakukan &n
membuat pointer menunjuk ke alamat X, oleh karena itu &n
memberitahu kita di mana n
berada.
(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
Ketika melakukan:
class C { var foo = 42, bar = 84 }
var c = C()
Memori dialokasikan di dua tempat:
Seperti yang dikatakan, kelas adalah tipe referensi: jadi nilai c
terletak di alamat X, di mana kita akan menemukan nilai Y. Dan di alamat Y + 16 kita akan menemukan foo
dan di alamat Y + 24 kita akan menemukan bar
( pada + 0 dan + 8 kita akan menemukan tipe data dan jumlah referensi, saya tidak bisa memberi tahu Anda lebih banyak tentang ini ...).
(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
adalah 42 (foo) dan 0x54
84 (bar).
Dalam kedua kasus, menggunakan &n
atau&c
akan memberi kami alamat X. Untuk tipe nilai, itulah yang kami inginkan, tetapi bukan untuk tipe referensi.
Ketika melakukan:
let referencePointer = UnsafeMutablePointer<C>(&c)
Kami membuat pointer pada referensi, yaitu pointer yang menunjuk ke alamat X. Hal yang sama saat menggunakan 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)
Sekarang kita memiliki pemahaman yang lebih baik tentang apa yang terjadi di bawah tenda, dan bahwa kita sekarang bahwa pada alamat X kita akan menemukan alamat Y (yang mana kita inginkan) kita dapat melakukan hal berikut untuk mendapatkannya:
let addressY = unsafeBitCast(c, to: Int.self)
Memverifikasi:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Ada cara lain untuk melakukan ini:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
sebenarnya menelepon unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Saya harap ini membantu ... itu berhasil untuk saya 😆.
MemoryLocation
menghasilkan 2 alamat yang berbeda.
===
operator identitas digunakan untuk memeriksa 2 objek yang menunjuk ke referensi yang sama.ObjectIdentifier
untuk mendapatkan alamat memoriclass 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
Gunakan ini saja:
print(String(format: "%p", object))
MyClass?
" tidak sesuai dengan CVarArg, Anda bisa melakukannya extension Optional : CVarArg { }
. Kalau tidak, ini sepertinya mencetak alamat tanpa semua kegilaan "tidak aman" dari jawaban lain.
var _cVarArgEncoding: [Int]
on CVarArg
. Tidak jelas bagaimana itu harus dilaksanakan.
Jika Anda hanya ingin melihat ini di debugger dan tidak melakukan hal lain dengan itu, tidak perlu benar-benar mendapatkan Int
pointer. Untuk mendapatkan representasi string dari alamat objek dalam memori, cukup gunakan sesuatu seperti ini:
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)
}
}
Contoh penggunaan:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Selain itu, ingatlah bahwa Anda cukup mencetak objek tanpa mengesampingkannya description
, dan itu akan menunjukkan alamat penunjuknya di samping teks yang lebih deskriptif (jika sering samar).
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()
bekerja dengan baik sebagai gantinya.
AnyObject
parameter. Saya lebih suka Any
sebagai tipe input.
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
.
Jawaban lain baik-baik saja, meskipun saya sedang mencari cara untuk mendapatkan alamat pointer sebagai integer:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Sedikit tindak lanjut.
Jawaban @Drew menyediakan hanya dapat digunakan untuk tipe kelas.
Jawaban @nschum hanya bisa untuk tipe struct.
Namun jika Anda menggunakan metode kedua untuk mendapatkan alamat array dengan elemen tipe nilai. Swift akan menyalin seluruh array karena dalam Swift array adalah copy-on-write dan Swift tidak dapat memastikannya berperilaku seperti ini setelah melewati kontrol ke C / C ++ (Yang dipicu dengan menggunakan &
untuk mendapatkan alamat). Dan jika Anda menggunakan metode pertama sebagai gantinya, itu akan secara otomatis dikonversi Array
ke NSArray
yang pasti sesuatu yang tidak kita inginkan.
Jadi cara paling sederhana dan terpadu yang saya temukan adalah menggunakan instruksi lldb frame variable -L yourVariableName
.
Atau Anda dapat menggabungkan jawaban mereka:
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
}
Ini untuk Swift 3.
Seperti @CharlieMonroe saya ingin mendapatkan alamat sebagai integer. Secara khusus, saya ingin alamat objek Thread untuk digunakan sebagai ID utas dalam modul logging diagnostik, untuk situasi di mana tidak ada nama utas yang tersedia.
Berdasarkan kode Charlie Monroe, inilah yang saya buat sejauh ini. Tetapi waspadalah, saya sangat baru di Swift, ini mungkin tidak benar ...
// 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
Pernyataan terakhir ada karena tanpa itu saya mendapatkan alamat seperti 0x60000007DB3F. Operasi modulo dalam pernyataan terakhir mengubahnya menjadi 0x7DB3F.
Solusi saya di Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
kode ini membuat deskripsi seperti deskripsi default
<MyClass: 0x610000223340>
Ini tentu bukan cara tercepat atau teraman untuk melakukannya. Tapi hal ini bekerja untukku. Ini akan memungkinkan subclass nsobject untuk mengadopsi properti ini.
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
harus berupa pointer. Dalam kode Swift Anda,str
bukan penunjuk. Jadi perbandingannya tidak berlaku.