Imprimir rapidamente um endereço de memória variável


166

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: ?")

2
Em [NSString stringWithFormat:@"%p", myVar], myVardeve ser um ponteiro. No seu código Swift, strnão é um ponteiro. Portanto, a comparação não se aplica.
user102008

5
Vale a pena notar que, pelo menos quando estou digitando isso, os dois comentários acima estão incorretos.
Ben leggiero

Respostas:


112

Swift 2

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

Swift 3

Para o Swift 3, use withUnsafePointer:

var str = "A String"
withUnsafePointer(to: &str) {
    print(" str value \(str) has address: \($0)")
}

2
unsafeAddressOf () funciona apenas para tipos de classe (como @Nick aponta abaixo). Portanto, essa resposta funciona apenas se o Foundation for importado e o String for ponte para o NSString. Na simples Swift, String é um tipo de valor e unsafeAddressOf não pode ser usado para obter seu endereço (a tentativa resulta em um erro de compilação).
Stephen Schaub

29
E se eu quiser imprimir um endereço de valor imutável com o Swift 3? withUnsafePointerresulta em cannot pass immutable value as inout argumenterro.
Alexander Vasenin

12
Mesmo sendo aceito e com a resposta mais votada, ainda dá resultados errados . Exemplo: print(self.description)impressões <MyViewController: 0x101c1d580>, usamos como referência. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }imprime 0x16fde4028um endereço claramente diferente. Alguém pode explicar o porquê?
Alexander Vasenin

4
BTW, isso imprime 0x101c1d580conforme o esperado:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
Alexander Vasenin

8
@nyg Sua resposta me deu uma pista para a causa real do erro: 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)!
Alexander Vasenin

214

Nota: Isto é para tipos de referência.

Swift 4/5:

print(Unmanaged.passUnretained(someVar).toOpaque())

Imprime o endereço de memória de someVar. (graças a @Ying)


Swift 3.1:

print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())

Imprime o endereço de memória de someVar.



2
Xcode 8.2.1 autocorrect está me dizendo que é agoraprint(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Jeff

2
Isso não seria verdade apenas se o someVar for um objeto. Se someVar for um tipo de valor, como uma struct, isso fornecerá um endereço diferente toda vez que for executado.
OutOnAWeekend

3
@OutOnAWeekend Você está certo, provavelmente porque a estrutura é copiada quando passada como argumento. Usando Unmanagedisso pode ser feito da seguinte forma: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque()).
nyg

1
A partir do Swift 4, eu era capaz de conseguir esse encantamento para imprimir, bem como - Unmanaged.passUnretained(someVar).toOpaque()(sem necessidade de especificação genérico)
Ying

2
A resposta do Swift 4 é perfeita. Se você também deseja obter a representação da string sem imprimir, recomendo adicionar debugDescriptionao final dela.
Patrick

51

Observe que essa resposta era bastante antiga. Muitos dos métodos descritos não funcionam mais. Especificamente .corenã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 takesIntestá 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:, 0x6000000530b0e 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

Sim, o ponteiro deve sair, mas não apenas por motivos de compatibilidade, como você consultou. os ponteiros geralmente usados ​​pelo sistema operacional. mesmo que o idioma seja de alto nível, os ponteiros devem existir em qualquer idioma e a qualquer momento no mecanismo do sistema operacional.
holex

Eu disse "por motivos de compatibilidade E porque o tempo de execução precisa disso" :-) a segunda instrução recapitula o que você está dizendo (suponho que um programador a conheça e entenda, então passei algumas palavras) #
2626 LombaX

Presumo que isso não se aplica mais?
precisa saber é o seguinte

Sim. Editei a resposta correta de @Drew para ela.
Rog

@LombaX, podemos apenas imprimir o endereço do tipo de referência? Não consigo imprimir o endereço das variáveis?
AbhimanyuAryan

21

Para obter o endereço (heap) de um objeto

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 ofor atribuído a outra variável o2, o endereço retornado o2será 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.

Para obter o endereço (pilha) de uma estrutura, tipo incorporado ou referência de objeto

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 sfor atribuído a outra variável s2, o valor será copiado e o endereço retornado s2será diferente.

Como ele se encaixa (recapitulação do ponteiro)

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)


Hmm. Obter o endereço da pilha da propriedade de um objeto não é consistente. Alguma ideia? Confira esta essência!
Aleclarson

As propriedades do objeto estão na pilha, não na pilha. Quando você passa a propriedade de uma instância de classe como UnsafePointer, o Swift realmente copia o valor primeiro e você obtém o endereço da cópia. Eu suspeito que isso evite que o código C contorne a interface do objeto e cause um estado inconsistente. Não sei se existe uma maneira de contornar isso.
Nschum 22/09


20

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 &ncria um ponteiro apontando para o endereço X e, portanto, &ninforma onde nestá 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:

  • no endereço Y, onde os dados da instância da classe estão localizados e
  • no endereço X, onde a referência da instância da classe está localizada.

Como dito, as classes são tipos de referência: portanto, o valor de cestá localizado no endereço X, no qual encontraremos o valor de Y. E no endereço Y + 16, encontraremos fooe 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 &nou &cnos 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 😆.


Só notei que, ao tentar imprimir o endereço da mesma estrutura através de 2 instâncias diferentes, MemoryLocationproduz 2 endereços diferentes.
user1046037

@ user1046037 Obrigado, fiz a alteração para a classe init. Também recebo dois endereços diferentes, mas somente quando uso uma estrutura vazia. Usar struct vazio sempre me dá resultados estranhos. Meu palpite é que o compilador faz algumas otimizações ...
nyg

@ user1046037 Verifique: pastebin.com/mpd3ujw2 . Aparentemente, todas as estruturas vazias apontam para o mesmo endereço de memória. No entanto, quando queremos armazenar o ponteiro em uma variável que irá criar uma cópia dela (ou do struct?) ...
nyg

Isso é interessante, mas quando impresso duas vezes, ele imprime os diferentes endereços de memória. O mesmo acontece com as respostas acima também.
user1046037

@ user1046037 Não sei ao certo o que você quer dizer, você tem algum código? (Eu sempre obter o mesmo endereço de memória)
nyg

15

Tipos de referência:

  • Faz sentido obter o endereço de memória de um tipo de referência, pois representa a identidade.
  • === O operador de identidade é usado para verificar 2 objetos que apontam para a mesma referência.
  • Use ObjectIdentifierpara obter o endereço de memória

Código:

class 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

Tipos de valor:

  • A necessidade de obter o endereço de memória de um tipo de valor não tem muita importância (pois é um valor) e a ênfase seria mais na igualdade do valor.

12

Apenas use isto:

print(String(format: "%p", object))

Se você receber uma reclamação do compilador sobre " 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.
Devin Lane,

Requer a implementação de var _cVarArgEncoding: [Int]on CVarArg. Não está claro como isso deve ser implementado.
Yuchen Zhong

10

Se você deseja apenas ver isso no depurador e não fazer mais nada com ele, não há necessidade de obter o Intponteiro. 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í- descriptionlo, 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...

1
Por favor, acompanhar quaisquer downvotes com um comentário explicando por que, então eu e qualquer outra pessoa que vem aqui sabe por que isso é uma má solução :)
Ben leggiero

1
Simples e arrumado!
badhanganesh

9

Swift 5

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)
    }
}

Uso:

print("FileManager.default: \(String.pointer(FileManager.default))")
// FileManager.default: 0x00007fff5c287698

print("nil: \(String.pointer(nil))")
// nil: nil

Esta resposta está incorreta. Se você criar duas instâncias da mesma classe, ele retornará o mesmo endereço de memória para as duas instâncias. Unmanaged.passUnretained(myObject).toOpaque()funciona corretamente.
Padraig

@Padraig Obrigado, atualizei com o seu código. Infelizmente, agora é necessário um AnyObjectparâmetro. Eu preferiria Anycomo o tipo de entrada.
neoneye

6

Em Swift4 sobre Matriz:

    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)
    }

1
salvou o meu dia. Deve haver um distintivo "o mais útil recente para aquisição"
Anton Tropashko 26/03

De fato, esta é a única abordagem que imprimiu o meu self?.array.
Rostyslav Druzhchenko

4

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.


Veja abaixo uma versão desta resposta para Swift 3.
RenniePet

3

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 Arraypara o NSArrayque 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
}

3

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.


3

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>


2

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

Obrigado pelo feedback Jeff, vou atualizar a resposta
Charlton Provatas

1
Deixa pra lá! Foi erro meu! Seu código funciona bem com uma classe Swift pura. Desculpe-me pelo erro.
Jeff Jeff
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.