Esiste un modo per simulare [NSString stringWithFormat:@"%p", myVar]
, da Objective-C, nel nuovo linguaggio Swift?
Per esempio:
let str = "A String"
println(" str value \(str) has address: ?")
Esiste un modo per simulare [NSString stringWithFormat:@"%p", myVar]
, da Objective-C, nel nuovo linguaggio Swift?
Per esempio:
let str = "A String"
println(" str value \(str) has address: ?")
Risposte:
Questo è ora parte della libreria standard: 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
Per Swift 3, utilizzare withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
risulta in cannot pass immutable value as inout argument
errore.
print(self.description)
stampe <MyViewController: 0x101c1d580>
, lo usiamo come riferimento. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
stampe con 0x16fde4028
indirizzo chiaramente diverso. Qualcuno può spiegare perché?
0x101c1d580
come previsto:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
è una struttura, quindi per stampare l'indirizzo a cui punta (e non la struttura stessa) devi stampare String(format: "%p", $0.pointee)
!
Nota: questo è per i tipi di riferimento.
print(Unmanaged.passUnretained(someVar).toOpaque())
Stampa l'indirizzo di memoria di someVar. (grazie a @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Stampa l'indirizzo di memoria di someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
si può fare in questo modo: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(non sono necessarie specifiche generiche)
debugDescription
alla fine.
Nota che questa risposta era piuttosto vecchia. Molti dei metodi descritti non funzionano più. In particolare .core
non è più possibile accedervi.
Tuttavia, la risposta di @ drew è corretta e semplice:
Questo fa ora parte della libreria standard: unsafeAddressOf.
Quindi la risposta alle tue domande è:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Ecco la risposta originale che è stata contrassegnata come corretta (per posteri / cortesia):
I puntatori rapidi "nascondono", ma esistono ancora sotto il cofano. (perché il runtime ne ha bisogno e per motivi di compatibilità con Objc e C)
Tuttavia, ci sono poche cose da sapere, ma prima come stampare l'indirizzo di memoria di una stringa Swift?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Questo stampa l'indirizzo di memoria della stringa, se apri XCode -> Debug Workflow -> Visualizza memoria e vai all'indirizzo stampato, vedrai i dati grezzi della stringa. Poiché si tratta di una stringa letterale, si tratta di un indirizzo di memoria all'interno della memoria del file binario (non stack o heap).
Tuttavia, se lo fai
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Questo sarà nello stack, perché la stringa viene creata in fase di esecuzione
NOTA: .core._baseAddress non è documentato, l'ho trovato cercando nella finestra di ispezione delle variabili e potrebbe essere nascosto in futuro
_baseAddress non è disponibile su tutti i tipi, qui un altro esempio con un CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Dov'è takesInt
una funzione di supporto C come questa
void takesInt(int *intptr)
{
printf("%p", intptr);
}
Sul lato Swift, questa funzione è takesInt(intptr: CMutablePointer<CInt>)
, quindi ci vuole un CMutablePointer a un CInt, e puoi ottenerlo con & varname
La funzione stampa 0x7fff5fbfed98
, e a questo indirizzo di memoria troverai 289 (in notazione esadecimale). Puoi cambiarne il contenuto con*intptr = 123456
Ora, alcune altre cose da sapere.
String, in breve tempo, è un tipo primitivo, non un oggetto.
CInt è un tipo Swift associato al tipo C int.
Se vuoi l'indirizzo di memoria di un oggetto, devi fare qualcosa di diverso.
Swift ha alcuni tipi di puntatore che possono essere utilizzati quando si interagisce con C, e puoi leggerli qui: Tipi di puntatore Swift
Inoltre, puoi capire di più su di essi esplorando la loro dichiarazione (cmd + clic sul tipo), per capire come convertire un tipo di puntatore in un altro
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
è una funzione di supporto C che ho creato, con questa implementazione
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Ancora una volta, viene stampato un esempio dell'indirizzo:, 0x6000000530b0
e se si passa attraverso la finestra di ispezione della memoria, si troverà il NSString
Una cosa che puoi fare con i puntatori in Swift (questo può essere fatto anche con i parametri inout)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Oppure, interagendo con 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
( Modifica: Swift 1.2 ora include una funzione simile chiamata unsafeAddressOf
.)
In Objective-C questo sarebbe [NSString stringWithFormat:@"%p", o]
.
o
è un riferimento all'istanza. Quindi, se o
assegnato a un'altra variabile o2
, l'indirizzo restituito per o2
sarà lo stesso.
Questo non si applica alle strutture (inclusi String
) e ai tipi primitivi (come Int
), perché quelli vivono direttamente nello stack. Ma possiamo recuperare la posizione nello 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
In Objective-C questo sarebbe [NSString stringWithFormat:@"%p", &o]
o [NSString stringWithFormat:@"%p", &i]
.
s
è struct. Quindi, se s
assegnato a un'altra variabile s2
, il valore verrà copiato e l'indirizzo restituito per s2
sarà diverso.
Come in Objective-C, sono associati due indirizzi diversi o
. Il primo è la posizione dell'oggetto, il secondo è la posizione del riferimento (o puntatore) sull'oggetto.
Sì, questo significa che il contenuto dell'indirizzo 0x7fff5fbfe658 è il numero 0x6100000011d0 come può dirci il debugger:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Quindi, tranne che per le stringhe che sono strutture, internamente tutto ciò funziona praticamente come in (Obiettivo-) C.
(Attuale a partire da 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
*/
( Gist )
In Swift abbiamo a che fare con tipi di valore (strutture) o tipi di riferimento (classi). Quando si fa:
let n = 42 // Int is a structure, i.e. value type
Parte della memoria è allocata all'indirizzo X, e a questo indirizzo troveremo il valore 42. Fare &n
crea un puntatore che punta all'indirizzo X, quindi &n
ci dice dove n
si trova.
(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
Quando si fa:
class C { var foo = 42, bar = 84 }
var c = C()
La memoria è allocata in due posti:
Come detto, le classi sono tipi di riferimento: quindi il valore di c
si trova all'indirizzo X, al quale troveremo il valore di Y. E all'indirizzo Y + 16 troveremo foo
e all'indirizzo Y + 24 troveremo bar
( a + 0 e + 8 troveremo i dati di tipo e i conteggi dei riferimenti, non posso dirti molto di più su questo ...).
(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 (pippo) e 0x54
è 84 (bar).
In entrambi i casi, utilizzando &n
o&c
ci fornirà l'indirizzo X. Per i tipi di valore, è quello che vogliamo, ma non per i tipi di riferimento.
Quando si fa:
let referencePointer = UnsafeMutablePointer<C>(&c)
Creiamo un puntatore sul riferimento, cioè un puntatore che punta a indirizzare X. Stessa cosa quando si usa 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)
Ora che abbiamo una migliore comprensione di ciò che accade sotto il cofano e che ora che all'indirizzo X troveremo l'indirizzo Y (che è quello che vogliamo) possiamo fare quanto segue per ottenerlo:
let addressY = unsafeBitCast(c, to: Int.self)
verifica:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Esistono altri modi per farlo:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
chiama davvero unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Spero che questo abbia aiutato ... lo ha fatto per me 😆.
MemoryLocation
produce 2 indirizzi diversi.
===
l'operatore di identità viene utilizzato per controllare 2 oggetti puntare allo stesso riferimento.ObjectIdentifier
per ottenere l'indirizzo di memoriaclass 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
Usa questo:
print(String(format: "%p", object))
MyClass?
" non conforme a CVarArg, puoi farlo extension Optional : CVarArg { }
. Altrimenti questo sembra stampare gli indirizzi senza tutta la follia "non sicura" delle altre risposte.
var _cVarArgEncoding: [Int]
on CVarArg
. Non è chiaro come dovrebbe essere implementato.
Se vuoi solo vedere questo nel debugger e non fare nient'altro con esso, non è necessario ottenerlo Int
puntatore. Per ottenere in memoria la rappresentazione in forma di stringa dell'indirizzo di un oggetto, basta usare qualcosa del genere:
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)
}
}
Esempio di utilizzo:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Inoltre, ricorda che puoi semplicemente stampare un oggetto senza sovrascriverlo description
e mostrerà il suo indirizzo di puntatore insieme a un testo più descrittivo (se spesso criptico).
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()
funziona correttamente invece.
AnyObject
parametro. Preferirei Any
come tipo di 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
.
Le altre risposte vanno bene, anche se stavo cercando un modo per ottenere l'indirizzo del puntatore come numero intero:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Solo un piccolo seguito.
La risposta fornita da @Drew può essere utilizzata solo per il tipo di classe.
La risposta fornita da @nschum può essere solo per il tipo di struttura.
Tuttavia, se si utilizza il secondo metodo per ottenere l'indirizzo di un array con elemento tipo valore. Swift copierà l'intero array perché in Swift array è copia su scrittura e Swift non può assicurarsi che si comporti in questo modo una volta passato il controllo su C / C ++ (che viene attivato usando &
per ottenere l'indirizzo). E se usi invece il primo metodo, si convertirà automaticamente in Array
quello NSArray
che è sicuramente qualcosa che non vogliamo.
Quindi il modo più semplice e unificato che ho trovato è usare l'istruzione lldb frame variable -L yourVariableName
.
Oppure puoi combinare le loro risposte:
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
}
Questo è per Swift 3.
Come @CharlieMonroe, volevo ottenere l'indirizzo come numero intero. In particolare, volevo che l'indirizzo di un oggetto Thread fosse utilizzato come ID thread in un modulo di registrazione diagnostica, per situazioni in cui non era disponibile il nome di thread.
Basato sul codice di Charlie Monroe, ecco cosa ho escogitato finora. Ma attenzione, sono molto nuovo su Swift, questo potrebbe non essere corretto ...
// 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
L'ultima affermazione è lì perché senza di essa stavo ottenendo indirizzi come 0x60000007DB3F. L'operazione modulo nell'ultima istruzione lo converte in 0x7DB3F.
La mia soluzione su Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
questo codice crea una descrizione come la descrizione predefinita
<MyClass: 0x610000223340>
Questo non è certamente il modo più veloce o più sicuro per farlo. Ma funziona per me. Ciò consentirà a qualsiasi sottoclasse nsobject di adottare questa proprietà.
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
deve essere un puntatore. Nel tuo codice Swift,str
non è un puntatore. Quindi il confronto non si applica.