Existe alguma maneira de simular o [NSString stringWithFormat:@"%p", myVar]
, do Objective-C, na nova linguagem Swift?
Por exemplo:
let str = "A String"
println(" str value \(str) has address: ?")
Existe alguma maneira de simular o [NSString stringWithFormat:@"%p", myVar]
, do Objective-C, na nova linguagem Swift?
Por exemplo:
let str = "A String"
println(" str value \(str) has address: ?")
Respostas:
Esta é agora parte da biblioteca padrão: 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 o Swift 3, use withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
resulta em cannot pass immutable value as inout argument
erro.
print(self.description)
impressões <MyViewController: 0x101c1d580>
, usamos como referência. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
imprime 0x16fde4028
um endereço claramente diferente. Alguém pode explicar o porquê?
0x101c1d580
conforme o esperado:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
é uma estrutura, portanto, para imprimir o endereço que está apontando (e não a estrutura em si), você precisa imprimir String(format: "%p", $0.pointee)
!
Nota: Isto é para tipos de referência.
print(Unmanaged.passUnretained(someVar).toOpaque())
Imprime o endereço de memória de someVar. (graças a @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Imprime o endereço de memória de someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
isso pode ser feito da seguinte forma: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(sem necessidade de especificação genérico)
debugDescription
ao final dela.
Observe que essa resposta era bastante antiga. Muitos dos métodos descritos não funcionam mais. Especificamente .core
não pode mais ser acessado.
No entanto, a resposta de @ drew é correta e simples:
Agora isso faz parte da biblioteca padrão: unsafeAddressOf.
Portanto, a resposta para suas perguntas é:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Aqui está a resposta original que foi marcada como correta (para posteridade / polidez):
Swift "esconde" ponteiros, mas eles ainda existem sob o capô. (porque o tempo de execução precisa dele e por motivos de compatibilidade com Objc e C)
Existem poucas coisas para saber, no entanto, mas primeiro como imprimir o endereço de memória de uma Swift String?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Isso imprime o endereço de memória da string, se você abrir XCode -> Debug Workflow -> View Memory e acessar o endereço impresso, verá os dados brutos da string. Como essa é uma string literal, é um endereço de memória dentro do armazenamento do binário (não pilha ou pilha).
No entanto, se você fizer
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Isso estará na pilha, porque a sequência é criada em tempo de execução
NOTA: .core._baseAddress não está documentado, achei-o no inspetor de variáveis e pode estar oculto no futuro
_baseAddress não está disponível em todos os tipos, aqui outro exemplo com um CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Onde takesInt
está uma função auxiliar C como esta
void takesInt(int *intptr)
{
printf("%p", intptr);
}
No lado Swift, essa função é takesInt(intptr: CMutablePointer<CInt>)
, portanto, leva um CMutablePointer para um CInt e você pode obtê-lo com & varname
A função imprime 0x7fff5fbfed98
, e neste endereço de memória você encontrará 289 (em notação hexadecimal). Você pode alterar seu conteúdo com*intptr = 123456
Agora, outras coisas a saber.
String, em rápida, é um tipo primitivo, não um objeto.
CInt é um tipo Swift mapeado para o tipo C int.
Se você deseja o endereço de memória de um objeto, precisa fazer algo diferente.
O Swift possui alguns tipos de ponteiros que podem ser usados ao interagir com C, e você pode ler sobre eles aqui: Tipos de ponteiros rápidos
Além disso, você pode entender mais sobre eles explorando sua declaração (cmd + clique no tipo) para entender como converter um tipo de ponteiro para outro
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
é uma função auxiliar C que eu criei, com esta implementação
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Novamente, um exemplo do endereço impresso:, 0x6000000530b0
e se você passar pelo inspetor de memória, encontrará seu NSString
Uma coisa que você pode fazer com os ponteiros no Swift (isso pode ser feito com parâmetros inout)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Ou, interagindo com 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
( Edit: Swift 1.2 agora inclui uma função semelhante chamada unsafeAddressOf
.)
No Objetivo-C, isso seria [NSString stringWithFormat:@"%p", o]
.
o
é uma referência à instância. Portanto, se o
for atribuído a outra variável o2
, o endereço retornado o2
será o mesmo.
Isso não se aplica a estruturas (incluindo String
) e tipos primitivos (como Int
), porque eles vivem diretamente na pilha. Mas podemos recuperar a localização na pilha.
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
No Objetivo-C, isso seria [NSString stringWithFormat:@"%p", &o]
ou [NSString stringWithFormat:@"%p", &i]
.
s
é struct. Portanto, se s
for atribuído a outra variável s2
, o valor será copiado e o endereço retornado s2
será diferente.
Como no Objective-C, existem dois endereços diferentes associados o
. A primeira é a localização do objeto, a segunda é a localização da referência (ou ponteiro) para o objeto.
Sim, isso significa que o conteúdo do endereço 0x7fff5fbfe658 é o número 0x6100000011d0, conforme o depurador pode nos dizer:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Portanto, exceto que strings são structs, internamente, tudo isso funciona da mesma forma que em (Objective-) C.
(Atual a partir do 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 )
No Swift, lidamos com tipos de valor (estruturas) ou tipos de referência (classes). Ao fazer:
let n = 42 // Int is a structure, i.e. value type
Alguma memória é alocada no endereço X e, nesse endereço, encontraremos o valor 42. Doing &n
cria um ponteiro apontando para o endereço X e, portanto, &n
informa onde n
está localizado.
(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
Ao fazer:
class C { var foo = 42, bar = 84 }
var c = C()
A memória é alocada em dois locais:
Como dito, as classes são tipos de referência: portanto, o valor de c
está localizado no endereço X, no qual encontraremos o valor de Y. E no endereço Y + 16, encontraremos foo
e no endereço Y + 24, encontraremos bar
( em + 0 e + 8, encontraremos dados de tipo e contagem de referência; não posso contar muito mais sobre isso ...).
(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) e 0x54
é 84 (bar).
Nos dois casos, usar &n
ou &c
nos fornecerá o endereço X. Para os tipos de valor, é isso que queremos, mas não para os tipos de referência.
Ao fazer:
let referencePointer = UnsafeMutablePointer<C>(&c)
Criamos um ponteiro na referência, ou seja, um ponteiro que aponta para o endereço X. A mesma coisa ao usar 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)
Agora que temos uma melhor compreensão do que acontece sob o capô e que agora no endereço X encontramos o endereço Y (que é o que queremos), podemos fazer o seguinte para obtê-lo:
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)
Existem outras maneiras de fazer isso:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
realmente chama unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Espero que isso tenha ajudado ... ajudou por mim 😆.
MemoryLocation
produz 2 endereços diferentes.
===
O operador de identidade é usado para verificar 2 objetos que apontam para a mesma referência.ObjectIdentifier
para obter o endereço de memóriaclass 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
Apenas use isto:
print(String(format: "%p", object))
MyClass?
" não estiver em conformidade com o CVarArg, poderá fazê-lo extension Optional : CVarArg { }
. Caso contrário, isso parece imprimir endereços sem toda a insanidade "insegura" das outras respostas.
var _cVarArgEncoding: [Int]
on CVarArg
. Não está claro como isso deve ser implementado.
Se você deseja apenas ver isso no depurador e não fazer mais nada com ele, não há necessidade de obter o Int
ponteiro. Para obter a representação em cadeia do endereço de um objeto na memória, use algo como isto:
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)
}
}
Exemplo de uso:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Além disso, lembre-se de que você pode simplesmente imprimir um objeto sem substituí- description
lo, e ele mostrará o endereço do ponteiro ao lado de um texto mais descritivo (se bem que enigmático).
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 corretamente.
AnyObject
parâmetro. Eu preferiria Any
como o 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
.
As outras respostas são boas, embora eu estivesse procurando uma maneira de obter o endereço do ponteiro como um número inteiro:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Apenas um pequeno acompanhamento.
A resposta que @Drew fornece pode ser usada apenas para o tipo de classe.
A resposta que @nschum fornece pode ser apenas para o tipo struct.
No entanto, se você usar o segundo método para obter o endereço de uma matriz com o elemento do tipo de valor. O Swift copiará toda a matriz porque na matriz Swift é cópia na gravação e a Swift não pode se comportar dessa maneira depois de passar o controle para o C / C ++ (que é acionado usando &
para obter o endereço). E se você usar o primeiro método, ele será convertido automaticamente Array
para o NSArray
que certamente é algo que não queremos.
Portanto, a maneira mais simples e unificada que encontrei é usar a instrução lldb frame variable -L yourVariableName
.
Ou você pode combinar as respostas deles:
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
}
Isto é para o Swift 3.
Como @CharlieMonroe, eu queria obter o endereço como um número inteiro. Especificamente, eu queria o endereço de um objeto Thread para uso como um ID de thread em um módulo de log de diagnóstico, para situações em que nenhum nome de thread estivesse disponível.
Baseado no código de Charlie Monroe, aqui está o que eu criei até agora. Mas cuidado, eu sou muito novo no Swift, isso pode não estar correto ...
// 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
A última declaração está lá porque, sem ela, eu estava obtendo endereços como 0x60000007DB3F. A operação do módulo na última instrução converte isso em 0x7DB3F.
Minha solução no Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
esse código cria descrição como descrição padrão
<MyClass: 0x610000223340>
Certamente, essa não é a maneira mais rápida ou segura de fazer isso. Mas funciona para mim. Isso permitirá que qualquer subclasse nsobject adote essa propriedade.
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 ser um ponteiro. No seu código Swift,str
não é um ponteiro. Portanto, a comparação não se aplica.