Gibt es überhaupt eine Möglichkeit, das [NSString stringWithFormat:@"%p", myVar]
von Objective-C in der neuen Swift-Sprache zu simulieren ?
Beispielsweise:
let str = "A String"
println(" str value \(str) has address: ?")
Gibt es überhaupt eine Möglichkeit, das [NSString stringWithFormat:@"%p", myVar]
von Objective-C in der neuen Swift-Sprache zu simulieren ?
Beispielsweise:
let str = "A String"
println(" str value \(str) has address: ?")
Antworten:
Dies ist jetzt Teil der Standardbibliothek : 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
Verwenden Sie für Swift 3 withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
führt zu einem cannot pass immutable value as inout argument
Fehler.
print(self.description)
Drucke <MyViewController: 0x101c1d580>
, wir verwenden es als Referenz. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
druckt 0x16fde4028
die deutlich andere Adresse. Kann jemand erklären warum?
0x101c1d580
print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
ist eine Struktur. Um also die Adresse zu drucken, auf die sie zeigt (und nicht die Struktur selbst), müssen Sie drucken String(format: "%p", $0.pointee)
!
Hinweis: Dies gilt für Referenztypen.
print(Unmanaged.passUnretained(someVar).toOpaque())
Druckt die Speicheradresse von someVar. (danke an @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Druckt die Speicheradresse von someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
kann folgendermaßen erfolgen : print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(keine generische Spezifikation erforderlich)
debugDescription
sie am Ende hinzuzufügen .
Beachten Sie, dass diese Antwort ziemlich alt war. Viele der beschriebenen Methoden funktionieren nicht mehr. Speziell.core
kann nicht mehr zugegriffen werden.
Die Antwort von @ draw ist jedoch richtig und einfach:
Dies ist jetzt Teil der Standardbibliothek: unsafeAddressOf.
Die Antwort auf Ihre Fragen lautet also:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Hier ist die ursprüngliche Antwort, die als richtig markiert wurde (für Nachwelt / Höflichkeit):
Schnelle "versteckt" Zeiger, aber sie existieren immer noch unter der Haube. (weil die Laufzeit es braucht und aus Kompatibilitätsgründen mit Objc und C)
Es gibt jedoch einige Dinge zu wissen, aber zuerst, wie man die Speicheradresse eines Swift-Strings druckt?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Dadurch wird die Speicheradresse der Zeichenfolge gedruckt. Wenn Sie XCode -> Debug-Workflow -> Speicher anzeigen öffnen und zur gedruckten Adresse wechseln, werden die Rohdaten der Zeichenfolge angezeigt. Da dies ein String-Literal ist, ist dies eine Speicheradresse im Speicher der Binärdatei (nicht Stapel oder Heap).
Wenn Sie dies jedoch tun
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Dies befindet sich auf dem Stapel, da die Zeichenfolge zur Laufzeit erstellt wird
HINWEIS: .core._baseAddress ist nicht dokumentiert. Ich habe festgestellt, dass es im Variableninspektor angezeigt wird und möglicherweise in Zukunft ausgeblendet wird
_baseAddress ist nicht für alle Typen verfügbar, hier ein weiteres Beispiel mit einem CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Wo takesInt
ist eine C-Helferfunktion wie diese?
void takesInt(int *intptr)
{
printf("%p", intptr);
}
Auf der Swift-Seite ist diese Funktion so takesInt(intptr: CMutablePointer<CInt>)
, dass ein CMutablePointer zu einem CInt führt und Sie ihn mit & varname erhalten können
Die Funktion wird gedruckt 0x7fff5fbfed98
, und an dieser Speicheradresse finden Sie 289 (in hexadezimaler Schreibweise). Sie können den Inhalt mit ändern*intptr = 123456
Nun noch ein paar andere Dinge zu wissen.
String ist schnell ein primitiver Typ, kein Objekt.
CInt ist ein Swift-Typ, der dem C int-Typ zugeordnet ist.
Wenn Sie die Speicheradresse eines Objekts möchten, müssen Sie etwas anderes tun.
Swift verfügt über einige Zeigertypen, die bei der Interaktion mit C verwendet werden
können. Weitere Informationen hierzu finden Sie hier: Swift-Zeigertypen
Darüber hinaus erfahren Sie mehr darüber, wie sie ihre Deklaration untersuchen (cmd + Klicken Sie auf den Typ), um zu verstehen, wie sie konvertiert werden eine Art Zeiger in eine andere
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
ist eine C-Hilfsfunktion, die ich mit dieser Implementierung erstellt habe
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Wieder ein Beispiel für die gedruckte Adresse: 0x6000000530b0
und wenn Sie den Speicherinspektor durchlaufen, finden Sie Ihren NSString
Eine Sache, die Sie mit Zeigern in Swift machen können (dies kann sogar mit inout-Parametern gemacht werden)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Oder mit Objc / c interagieren
// 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
( Bearbeiten: Swift 1.2 enthält jetzt eine ähnliche Funktion namensunsafeAddressOf
.)
In Objective-C wäre dies [NSString stringWithFormat:@"%p", o]
.
o
ist ein Verweis auf die Instanz. Wenn o
also eine andere Variable zugewiesen ist o2
, ist die zurückgegebene Adresse für o2
dieselbe.
Dies gilt nicht für Strukturen (einschließlich String
) und primitive Typen (wie Int
), da diese direkt auf dem Stapel leben. Wir können jedoch den Speicherort auf dem Stapel abrufen.
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
In Objective-C wäre dies [NSString stringWithFormat:@"%p", &o]
oder [NSString stringWithFormat:@"%p", &i]
.
s
ist struct. Wenn s
also eine andere Variable zugewiesen wird s2
, wird der Wert kopiert und die zurückgegebene Adresse für s2
ist unterschiedlich.
Wie in Objective-C sind zwei verschiedene Adressen zugeordnet o
. Der erste ist der Ort des Objekts, der zweite ist der Ort der Referenz (oder des Zeigers) auf das Objekt.
Ja, dies bedeutet, dass der Inhalt der Adresse 0x7fff5fbfe658 die Nummer 0x6100000011d0 ist, wie der Debugger uns mitteilen kann:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Abgesehen davon, dass Strings Strukturen sind, funktioniert dies intern ziemlich genau so wie in (Objective-) C.
(Stand 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
*/
( Kern )
In Swift beschäftigen wir uns entweder mit Werttypen (Strukturen) oder Referenztypen (Klassen). Wenn Sie:
let n = 42 // Int is a structure, i.e. value type
An der Adresse X wird etwas Speicher zugewiesen, und an dieser Adresse finden wir den Wert 42. Wenn Sie dies tun, &n
wird ein Zeiger erstellt, der auf die Adresse X zeigt, und es &n
wird uns mitgeteilt, wo n
sich dieser befindet.
(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
Wenn Sie:
class C { var foo = 42, bar = 84 }
var c = C()
Der Speicher wird an zwei Stellen zugewiesen:
Wie gesagt, Klassen sind Referenztypen: Der Wert von c
befindet sich also an der Adresse X, an der wir den Wert von Y finden. Und an der Adresse Y + 16 finden wir foo
und an der Adresse Y + 24 finden wir bar
( Bei + 0 und + 8 finden wir Typdaten und Referenzzählungen. Mehr kann ich dazu nicht sagen ...).
(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
ist 42 (foo) und 0x54
ist 84 (bar).
In beiden Fällen mit &n
oder&c
wir geben uns die Adresse X. Für Werttypen ist dies das, was wir wollen, aber nicht für Referenztypen.
Wenn Sie:
let referencePointer = UnsafeMutablePointer<C>(&c)
Wir erstellen einen Zeiger auf die Referenz, dh einen Zeiger, der auf die Adresse X zeigt. Dasselbe gilt bei der Verwendung 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)
Jetzt, da wir besser verstehen, was unter der Haube vor sich geht, und jetzt, wo wir unter Adresse X die Adresse Y finden (die wir wollen), können wir Folgendes tun, um sie zu erhalten:
let addressY = unsafeBitCast(c, to: Int.self)
Überprüfen:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Es gibt andere Möglichkeiten, dies zu tun:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
ruft tatsächlich an unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Ich hoffe das hat geholfen ... es hat mir geholfen 😆.
MemoryLocation
2 verschiedene Adressen erzeugt werden.
===
Der Identitätsoperator wird verwendet, um zu überprüfen, ob 2 Objekte auf dieselbe Referenz verweisen.ObjectIdentifier
diese Option , um die Speicheradresse abzurufenclass 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
Verwenden Sie einfach dies:
print(String(format: "%p", object))
MyClass?
" nicht mit CVarArg übereinstimmt, können Sie dies tun extension Optional : CVarArg { }
. Andernfalls scheint dies Adressen zu drucken, ohne den "unsicheren" Wahnsinn der anderen Antworten.
var _cVarArgEncoding: [Int]
on CVarArg
. Nicht klar, wie das umgesetzt werden soll.
Wenn Sie dies nur im Debugger sehen und nichts anderes damit tun möchten, müssen Sie den Int
Zeiger nicht tatsächlich abrufen. Verwenden Sie Folgendes, um die Zeichenfolgendarstellung der Adresse eines Objekts im Speicher abzurufen:
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)
}
}
Anwendungsbeispiel:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Denken Sie außerdem daran, dass Sie ein Objekt einfach drucken können, ohne es zu überschreiben description
, und dass seine Zeigeradresse neben aussagekräftigerem (wenn auch häufig kryptischem) Text angezeigt wird.
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()
funktioniert stattdessen richtig.
AnyObject
Parameter. Ich würde Any
als Eingabetyp bevorzugen .
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
.
Die anderen Antworten sind in Ordnung, obwohl ich nach einer Möglichkeit gesucht habe, die Zeigeradresse als Ganzzahl zu erhalten:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Nur ein kleines Follow-up.
Die Antwort @Drew kann nur für den Klassentyp verwendet werden.
Die Antwort @nschum kann nur für den Strukturtyp sein.
Wenn Sie jedoch die zweite Methode verwenden, um die Adresse eines Arrays mit dem Werttypelement abzurufen. Swift kopiert das gesamte Array, da das Swift-Array beim Schreiben kopiert wird und Swift nicht sicherstellen kann, dass es sich so verhält, wenn es die Kontrolle an C / C ++ übergibt (was durch Abrufen &
der Adresse ausgelöst wird ). Und wenn Sie stattdessen die erste Methode verwenden, wird diese automatisch konvertiert Array
, NSArray
was wir sicherlich nicht wollen.
Der einfachste und einheitlichste Weg, den ich gefunden habe, ist die Verwendung von lldb-Anweisungen frame variable -L yourVariableName
.
Oder Sie können ihre Antworten kombinieren:
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
}
Dies ist für Swift 3.
Wie bei @CharlieMonroe wollte ich die Adresse als Ganzzahl erhalten. Insbesondere wollte ich, dass die Adresse eines Thread-Objekts als Thread-ID in einem Diagnoseprotokollierungsmodul verwendet wird, wenn kein Thread-Name verfügbar ist.
Basierend auf Charlie Monroes Code habe ich mir Folgendes ausgedacht. Aber Vorsicht, ich bin sehr neu bei Swift, das ist möglicherweise nicht richtig ...
// 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
Die letzte Anweisung ist da, weil ich ohne sie Adressen wie 0x60000007DB3F bekommen habe. Die Modulo-Operation in der letzten Anweisung konvertiert diese in 0x7DB3F.
Meine Lösung für Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
Dieser Code erstellt eine Beschreibung wie die Standardbeschreibung
<MyClass: 0x610000223340>
Dies ist sicherlich nicht der schnellste oder sicherste Weg, dies zu tun. Aber es funktioniert bei mir. Dadurch kann jede nsobject-Unterklasse diese Eigenschaft übernehmen.
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
muss ein Zeiger sein. Ist in Ihrem Swift-Codestr
kein Zeiger. Der Vergleich trifft also nicht zu.