¿Hay alguna forma de simular [NSString stringWithFormat:@"%p", myVar]
, desde Objective-C, en el nuevo lenguaje Swift?
Por ejemplo:
let str = "A String"
println(" str value \(str) has address: ?")
¿Hay alguna forma de simular [NSString stringWithFormat:@"%p", myVar]
, desde Objective-C, en el nuevo lenguaje Swift?
Por ejemplo:
let str = "A String"
println(" str value \(str) has address: ?")
Respuestas:
Esto es ahora parte de la biblioteca estándar: 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
Para Swift 3, use withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
da como resultado un cannot pass immutable value as inout argument
error.
print(self.description)
impresiones <MyViewController: 0x101c1d580>
, lo usamos como referencia. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
Impresiones 0x16fde4028
que es claramente diferente dirección. ¿Alguien puede explicar por qué?
0x101c1d580
como se esperaba:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
es una estructura, por lo que para imprimir la dirección a la que apunta (y no la estructura en sí) ¡tiene que imprimir String(format: "%p", $0.pointee)
!
Nota: Esto es para tipos de referencia.
print(Unmanaged.passUnretained(someVar).toOpaque())
Imprime la dirección de memoria de someVar. (gracias a @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Imprime la dirección de memoria de someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
que se puede hacer así: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(sin necesidad de una especificación genérica)
debugDescription
al final.
Tenga en cuenta que esta respuesta era bastante antigua. Muchos de los métodos que describe ya no funcionan. Específicamente .core
ya no se puede acceder.
Sin embargo, la respuesta de @ drew es correcta y simple:
Esto ahora es parte de la biblioteca estándar: unsafeAddressOf.
Entonces la respuesta a sus preguntas es:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Aquí está la respuesta original que se marcó correcta (para posteridad / cortesía):
Los punteros rápidos "ocultan", pero todavía existen debajo del capó. (porque el tiempo de ejecución lo necesita, y por razones de compatibilidad con Objc y C)
Sin embargo, hay algunas cosas que debe saber, pero primero, ¿cómo imprimir la dirección de memoria de una cadena rápida?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Esto imprime la dirección de memoria de la cadena, si abre XCode -> Debug Workflow -> View Memory y va a la dirección impresa, verá los datos sin procesar de la cadena. Como se trata de un literal de cadena, se trata de una dirección de memoria dentro del almacenamiento del binario (no de pila o montón).
Sin embargo, si lo haces
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Esto estará en la pila, porque la cadena se crea en tiempo de ejecución
NOTA: .core._baseAddress no está documentado, lo encontré buscando en el inspector de variables y puede estar oculto en el futuro
_baseAddress no está disponible en todos los tipos, aquí otro ejemplo con un CInt
var testNumber : CInt = 289
takesInt(&testNumber)
¿Dónde takesInt
está una función auxiliar de C como esta?
void takesInt(int *intptr)
{
printf("%p", intptr);
}
En el lado de Swift, esta función es takesInt(intptr: CMutablePointer<CInt>)
, por lo que toma un CMutablePointer a un CInt, y puede obtenerlo con & varname
La función se imprime 0x7fff5fbfed98
, y en esta dirección de memoria encontrará 289 (en notación hexadecimal). Puedes cambiar su contenido con*intptr = 123456
Ahora, algunas otras cosas para saber.
La cadena, en forma rápida, es un tipo primitivo, no un objeto.
CInt es un tipo Swift asignado al tipo C int.
Si desea la dirección de memoria de un objeto, debe hacer algo diferente.
Swift tiene algunos tipos de puntero que se pueden usar al interactuar con C, y puede leer sobre ellos aquí: Tipos de puntero Swift
Además, puede comprender más acerca de cómo explorar su declaración (cmd + clic en el tipo), para comprender cómo convertir un tipo de puntero a otro
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
es una función auxiliar de C que creé, con esta implementación
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Nuevamente, un ejemplo de la dirección impresa: 0x6000000530b0
y si pasa por el inspector de memoria encontrará su NSString
Una cosa que puedes hacer con punteros en Swift (esto incluso se puede hacer con parámetros inout)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
O, interactuando 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
( Editar: Swift 1.2 ahora incluye una función similar llamada unsafeAddressOf
).
En Objective-C esto sería [NSString stringWithFormat:@"%p", o]
.
o
es una referencia a la instancia. Entonces, si o
se asigna a otra variable o2
, la dirección devuelta o2
será la misma.
Esto no se aplica a estructuras (incluidos String
) y tipos primitivos (como Int
), porque esos viven directamente en la pila. Pero podemos recuperar la ubicación en la pila.
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
En Objective-C esto sería [NSString stringWithFormat:@"%p", &o]
o [NSString stringWithFormat:@"%p", &i]
.
s
es struct. Entonces, si s
se asigna a otra variable s2
, el valor se copiará y la dirección devuelta s2
será diferente.
Al igual que en Objective-C, hay dos direcciones diferentes asociadas con o
. El primero es la ubicación del objeto, el segundo es la ubicación de la referencia (o puntero) al objeto.
Sí, esto significa que el contenido de la dirección 0x7fff5fbfe658 es el número 0x6100000011d0 como el depurador puede decirnos:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Entonces, excepto que las cadenas son estructuras, internamente todo esto funciona casi igual que en (Objective-) C.
(Actual a partir de 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
*/
( Esencia )
En Swift tratamos con tipos de valor (estructuras) o tipos de referencia (clases). Al hacer:
let n = 42 // Int is a structure, i.e. value type
Se asigna algo de memoria en la dirección X, y en esta dirección encontraremos el valor 42. Hacer &n
crea un puntero que apunta a la dirección X, por lo tanto, &n
nos dice dónde n
está ubicado.
(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
Al hacer:
class C { var foo = 42, bar = 84 }
var c = C()
La memoria se asigna en dos lugares:
Como se dijo, las clases son tipos de referencia: por lo tanto, el valor de c
se encuentra en la dirección X, donde encontraremos el valor de Y. Y en la dirección Y + 16 encontraremos foo
y en la dirección Y + 24 encontraremos bar
( en + 0 y + 8 encontraremos datos de tipo y recuentos de referencias, no puedo decirle mucho más sobre esto ...).
(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
es 42 (foo) y 0x54
es 84 (bar).
En ambos casos, usar &n
o &c
nos dará la dirección X. Para los tipos de valor, eso es lo que queremos, pero no para los tipos de referencia.
Al hacer:
let referencePointer = UnsafeMutablePointer<C>(&c)
Creamos un puntero en la referencia, es decir, un puntero que apunta a la dirección X. Lo mismo cuando se 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)
Ahora que tenemos una mejor comprensión de lo que sucede debajo del capó, y ahora que en la dirección X encontraremos la dirección Y (que es la que queremos), podemos hacer lo siguiente para obtenerla:
let addressY = unsafeBitCast(c, to: Int.self)
Verificando:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Hay otras formas de hacer esto:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
En realidad llama unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Espero que esto haya ayudado ... lo hizo para mí 😆.
MemoryLocation
produce 2 direcciones diferentes.
===
El operador de identidad se utiliza para comprobar que 2 objetos apuntan a la misma referencia.ObjectIdentifier
para obtener la dirección de 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
Solo usa esto:
print(String(format: "%p", object))
MyClass?
" no se ajusta a CVarArg, puede hacerlo extension Optional : CVarArg { }
. De lo contrario, esto parece imprimir direcciones sin toda la locura "insegura" de las otras respuestas.
var _cVarArgEncoding: [Int]
on CVarArg
. No está claro cómo se debe implementar eso.
Si solo quiere ver esto en el depurador y no hacer nada más con él, no hay necesidad de obtener el Int
puntero. Para obtener la representación de cadena de la dirección de un objeto en la memoria, simplemente use algo como esto:
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)
}
}
Ejemplo de uso:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Además, recuerde que simplemente puede imprimir un objeto sin anularlo description
, y mostrará su dirección de puntero junto con un texto más descriptivo (aunque a menudo críptico).
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()
funciona correctamente en su lugar.
AnyObject
parámetro. Preferiría Any
como el tipo de entrada.
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
.
Las otras respuestas están bien, aunque estaba buscando una manera de obtener la dirección del puntero como un entero:
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 pequeño seguimiento.
La respuesta que proporciona @Drew solo se puede usar para el tipo de clase.
La respuesta que proporciona @nschum solo puede ser para el tipo de estructura.
Sin embargo, si utiliza el segundo método para obtener la dirección de una matriz con un elemento de tipo de valor. Swift copiará toda la matriz porque en la matriz Swift se copia y escribe y Swift no puede asegurarse de que se comporte de esta manera una vez que pasa el control a C / C ++ (que se activa utilizando &
para obtener la dirección). Y si usa el primer método, se convertirá automáticamente Array
en NSArray
algo que seguramente no queremos.
Entonces, la forma más simple y unificada que encontré es usando la instrucción lldb frame variable -L yourVariableName
.
O puedes combinar sus respuestas:
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
}
Esto es para Swift 3.
Al igual que @CharlieMonroe, quería obtener la dirección como un número entero. Específicamente, quería la dirección de un objeto Thread para usar como ID de hilo en un módulo de registro de diagnóstico, para situaciones en las que no había un nombre de hilo disponible.
Basado en el código de Charlie Monroe, esto es lo que se me ocurrió hasta ahora. Pero cuidado, soy muy nuevo en Swift, esto puede no ser correcto ...
// 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
La última declaración está ahí porque sin ella estaba obteniendo direcciones como 0x60000007DB3F. La operación de módulo en la última instrucción convierte eso en 0x7DB3F.
Mi solución en Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
este código crea una descripción como la descripción predeterminada
<MyClass: 0x610000223340>
Ciertamente, esta no es la forma más rápida o segura de hacerlo. Pero funciona para mí. Esto permitirá que cualquier subclase nsobject adopte esta propiedad.
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
debe ser un puntero. En su código Swift,str
no es un puntero. Entonces la comparación no se aplica.