Печать адреса переменной памяти в swift

166

Есть ли способ имитировать [NSString stringWithFormat:@"%p", myVar], из Objective-C, в новом языке Swift?

Например:

let str = "A String"
println(" str value \(str) has address: ?")
apouche
источник
2
В [NSString stringWithFormat:@"%p", myVar], myVarдолжен быть указатель. В вашем коде Swift strнет указателя. Таким образом, сравнение не относится.
user102008
5
Стоит отметить, что, по крайней мере, когда я набираю это, два приведенных выше комментария неверны.
Бен Легжеро

Ответы:

112

Swift 2

Это теперь является частью стандартной библиотеки: 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

Для Swift 3 используйте withUnsafePointer:

var str = "A String"
withUnsafePointer(to: &str) {
    print(" str value \(str) has address: \($0)")
}
Нарисовался
источник
2
unsafeAddressOf () работает только для типов классов (как указывает @Nick ниже). Таким образом, этот ответ работает, только если Foundation импортирован и String соединен с NSString. В простом Swift String является типом значения, и unsafeAddressOf не может использоваться для получения его адреса (попытка приводит к ошибке компиляции).
Стивен Шауб
29
Что если я хочу напечатать адрес неизменного значения с помощью Swift 3? withUnsafePointerприводит к cannot pass immutable value as inout argumentошибке.
Александр Васенин
12
Несмотря на то, что это принятый и получивший наибольшее количество голосов ответ, он все равно дает неправильные результаты. Пример: print(self.description)печать <MyViewController: 0x101c1d580>, мы используем это как ссылку. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }печатает, 0x16fde4028что явно другой адрес. Кто-нибудь может объяснить, почему?
Александр Васенин
4
Кстати, это печатается, 0x101c1d580как ожидалось:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
Александр Васенин
8
@nyg Ваш ответ дал мне подсказку о фактической причине ошибки: UnsafePointerэто структура, поэтому для того, чтобы напечатать адрес, на который она указывает (а не саму структуру), вы должны напечатать String(format: "%p", $0.pointee)!
Александр Васенин
214

Примечание: это для справочных типов.

Свифт 4/5:

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

Печатает адрес памяти someVar. (спасибо @Ying)


Swift 3.1:

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

Печатает адрес памяти someVar.


Вудсток
источник
2
Xcode 8.2.1 автозамена говорит мне, что это сейчасprint(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Джефф
2
Не правда ли, только если someVar является объектом. Если someVar является типом значения, таким как структура, он будет давать новый адрес каждый раз при выполнении.
OutOnAWekend
3
@OutOnAWeekend Вы правы, скорее всего, потому что структура копируется при передаче в качестве аргумента. Используя Unmanagedэто можно сделать так: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque()).
nyg
1
Начиная со Swift 4, я смог также напечатать это заклинание - Unmanaged.passUnretained(someVar).toOpaque()(не нужно обобщенной спецификации)
Ying
2
Ответ Swift 4 идеален. Если вы также хотите получить строковое представление без печати, я бы порекомендовал добавить debugDescriptionего в конец.
Патрик
51

Обратите внимание, что этот ответ был довольно старым. Многие из описанных методов больше не работают. Конкретно .coreбольше нельзя получить доступ.

Однако ответ @ drew является правильным и простым:

Теперь это часть стандартной библиотеки: unsafeAddressOf.

Итак, ответ на ваши вопросы:

println(" str value \(str) has address: \(unsafeAddressOf(str))")

Вот оригинальный ответ, который был помечен правильно (для потомков / вежливости):

Свифт «прячет» указатели, но они все еще существуют под капотом. (потому что это требуется среде выполнения и по причинам совместимости с Objc и C)

Однако есть кое-что знать, но сначала как напечатать адрес памяти строки Swift?

    var aString : String = "THIS IS A STRING"
    NSLog("%p", aString.core._baseAddress)  // _baseAddress is a COpaquePointer
   // example printed address 0x100006db0

Это печатает адрес памяти строки, если вы откроете XCode -> Debug Workflow -> View Memory и перейдете к напечатанному адресу, вы увидите необработанные данные строки. Поскольку это строковый литерал, это адрес памяти внутри хранилища двоичного файла (а не стека или кучи).

Тем не менее, если вы делаете

    var aString : String = "THIS IS A STRING" + "This is another String"
    NSLog("%p", aString.core._baseAddress)

    // example printed address 0x103f30020

Это будет в стеке, потому что строка создается во время выполнения

ПРИМЕЧАНИЕ. .Core._baseAddress не задокументирован, я нашел его в инспекторе переменных, и он может быть скрыт в будущем

_baseAddress доступен не для всех типов, здесь еще один пример с CInt

    var testNumber : CInt = 289
    takesInt(&testNumber)

Где takesIntтакая вспомогательная функция C?

void takesInt(int *intptr)
{
    printf("%p", intptr);
}

Со стороны Swift эта функция есть takesInt(intptr: CMutablePointer<CInt>), поэтому она передает CMutablePointer в CInt, и вы можете получить его с помощью & varname

Функция печатает 0x7fff5fbfed98, и по этому адресу памяти вы найдете 289 (в шестнадцатеричном формате). Вы можете изменить его содержимое с помощью*intptr = 123456

Теперь еще кое-что нужно знать.

Строка, по-быстрому, является примитивным типом, а не объектом.
CInt - это тип Swift, сопоставленный с типом C int.
Если вы хотите адрес памяти объекта, вы должны сделать что-то другое.
У Swift есть несколько типов указателей, которые можно использовать при взаимодействии с C, и вы можете прочитать о них здесь: Типы указателей Swift
Более того, вы можете больше узнать о них, изучая их объявление (cmd + click на типе), чтобы понять, как конвертировать тип указателя на другой

    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 это вспомогательная функция C, которую я создал, с этой реализацией

void printptr(void ** ptr)
{
    printf("%p", *ptr);
}

Опять же, напечатан пример адреса:, 0x6000000530b0и если вы пройдете инспектор памяти, вы найдете свою NSString

Одна вещь, которую вы можете сделать с указателями в Swift (это может быть сделано даже с параметрами inout)

    func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>) 
    {
        stringa.memory = "String Updated";
    }

    var testString : NSString = "test string"
    println(testString)
    playWithPointer(&testString)
    println(testString)

Или, взаимодействуя с 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
LombaX
источник
да, указатель должен выходить, но не только по причинам совместимости, на которые вы ссылались. указатели, обычно используемые операционной системой. даже если язык является языком высокого уровня, указатели должны существовать на любом языке в любое время в движке операционной системы.
Holex
Я сказал «по причине совместимости И потому, что среда выполнения нуждается в этом» :-) Второе утверждение повторяет то, что вы говорите (я предполагаю, что программист знает и понимает это, поэтому я потратил несколько слов)
LombaX
Я полагаю, это больше не применимо?
aleclarson
Да. Я отредактировал правильный ответ от @Drew в него.
Рог
@LombaX мы можем печатать только адреса ссылочного типа? Я не могу напечатать адрес переменных?
АбхиманьюАрян
21

Чтобы получить (куча) адрес объекта

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 теперь включает в себя аналогичную функцию unsafeAddressOf.)

В Objective-C это было бы [NSString stringWithFormat:@"%p", o].

oэто ссылка на экземпляр. Таким образом, если oназначена другая переменная o2, возвращаемый адрес для o2будет таким же.

Это не относится к структурам (в том числе String) и примитивным типам (например Int), потому что они живут прямо в стеке. Но мы можем получить местоположение в стеке.

Чтобы получить (стек) адрес структуры, встроенного типа или ссылки на объект

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

В Objective-C это будет [NSString stringWithFormat:@"%p", &o]или [NSString stringWithFormat:@"%p", &i].

sэто структура. Таким образом, если sназначена другая переменная s2, значение будет скопировано, а возвращаемый адрес для s2будет другим.

Как это совмещается (резюме указателя)

Как и в Objective-C, есть два разных адреса, связанных с o. Первый - это местоположение объекта, второй - это местоположение ссылки (или указателя) на объект.

Да, это означает, что содержимое адреса 0x7fff5fbfe658 является числом 0x6100000011d0, как отладчик может сказать нам:

(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0

Таким образом, за исключением строк, являющихся структурами, внутренне все это работает почти так же, как в (Objective-) C.

(Текущий на Xcode 6.3)

nschum
источник
Хм. Получение стекового адреса свойства объекта не согласовано. Любые идеи? Проверьте эту суть!
aleclarson
Свойства объекта находятся в куче, а не в стеке. Когда вы передаете свойство экземпляра класса как UnsafePointer, Swift фактически сначала копирует значение, и вы получаете адрес копии. Я подозреваю, что это должно препятствовать тому, чтобы код C обошел интерфейс объекта и вызвал противоречивое состояние. Я не знаю, есть ли способ обойти это.
nschum
Вот тема, которую я создал на форумах разработчиков Apple . Там есть несколько хороших ответов.
aleclarson
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
*/

( Гист )


В Swift мы имеем дело с типами значений (структурами) или ссылочными типами (классами). При выполнении:

let n = 42 // Int is a structure, i.e. value type

Некоторая память выделяется по адресу X, и по этому адресу мы найдем значение 42. Выполнение &nсоздает указатель, указывающий на адрес X, поэтому &nсообщает нам, где nнаходится.

(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

При выполнении:

class C { var foo = 42, bar = 84 }
var c = C()

Память выделяется в двух местах:

  • по адресу Y, где расположены данные экземпляра класса и
  • по адресу X, где находится ссылка на экземпляр класса.

Как уже говорилось, классы являются ссылочными типами: поэтому значение cнаходится по адресу X, по которому мы найдем значение Y. А по адресу Y + 16 мы найдем fooи по адресу Y + 24 мы найдем bar( в + 0 и + 8 мы найдем данные о типе и количество ссылок, я не могу рассказать вам больше об этом ...).

(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

0x2a42 (foo) и 0x5484 (бар).

В обоих случаях использование &nили &cдаст нам адрес X. Для типов значений это то, что мы хотим, но не для ссылочных типов.

При выполнении:

let referencePointer = UnsafeMutablePointer<C>(&c)

Мы создаем указатель на ссылку, то есть указатель, который указывает на адрес X. То же самое при использовании 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)

Теперь, когда мы лучше понимаем, что происходит под капотом, и теперь мы знаем, что по адресу X мы найдем адрес Y (именно тот, который нам нужен), мы можем сделать следующее, чтобы получить его:

let addressY = unsafeBitCast(c, to: Int.self)

Проверка:

(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)

Есть и другие способы сделать это:

let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }

toOpaque()на самом деле звонки unsafeBitCast(c, to: UnsafeMutableRawPointer.self).

Я надеюсь, что это помогло ... это сделало для меня 😆.

nyg
источник
Просто заметил, что при попытке напечатать адрес одной и той же структуры через 2 разных экземпляра MemoryLocationвыдает 2 разных адреса.
user1046037
@ user1046037 Спасибо, внес изменения для класса init. Я также получаю два разных адреса, но только когда использую пустую структуру. Использование пустой структуры всегда дает мне странные результаты. Я предполагаю, что компилятор делает некоторые оптимизации ...
Nyg
@ user1046037 Проверьте: pastebin.com/mpd3ujw2 . По-видимому, все пустые структуры указывают на один и тот же адрес памяти. Однако, когда мы хотим сохранить указатель в переменной, он создаст его копию (или структуру?) ...
nyg
Это интересно, но при печати дважды печатает разные адреса памяти. То же самое и с ответами выше.
user1046037
@ user1046037 Я не уверен, что понимаю, что вы имеете в виду, у вас есть код? (Я всегда получаю один и тот же адрес памяти)
nyg
15

Типы ссылок:

  • Имеет смысл получить адрес памяти ссылочного типа, поскольку он представляет идентичность.
  • === Оператор идентификации используется для проверки того, что 2 объекта указывают на одну и ту же ссылку.
  • Используйте, ObjectIdentifierчтобы получить адрес памяти

Код:

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

Типы значений:

  • Необходимость получения адреса памяти типа значения не имеет большого значения (поскольку это значение), и акцент будет сделан больше на равенстве значения.
user1046037
источник
12

Просто используйте это:

print(String(format: "%p", object))
pavelcauselov
источник
Если вы получили жалобу компилятора о том, что " MyClass?" не соответствует CVarArg, вы можете это сделать extension Optional : CVarArg { }. В противном случае это, кажется, печатает адреса без всего "небезопасного" безумия других ответов.
Девин Лейн
Требуется реализация var _cVarArgEncoding: [Int]по CVarArg. Не ясно, как это должно быть реализовано.
Юйчен Чжун
10

Если вы просто хотите увидеть это в отладчике и больше ничего не делать с ним, нет необходимости фактически получать Intуказатель. Чтобы получить строковое представление адреса объекта в памяти, просто используйте что-то вроде этого:

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

Пример использования:

print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...

Кроме того, помните, что вы можете просто напечатать объект, не переопределяя его description, и он покажет адрес своего указателя вместе с более информативным (если часто загадочным) текстом.

print(self, "Doing something else...")
// Prints like: <MyModule.MyClass: 0x7fd190d0f270> Doing something else...
// Sometimes like: <_TtCC14__lldb_expr_668MyModule7MyClass: 0x7fd190d0f270> Doing something else...
Бен Легжеро
источник
1
Пожалуйста, сопровождайте любые отрицательные отзывы комментарием, объясняющим почему, поэтому я и любой другой человек, приходящий сюда, знают, почему это плохое решение :)
Бен Легжеро
1
Просто и аккуратно!
Бадханганеш
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)
    }
}

Использование:

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

print("nil: \(String.pointer(nil))")
// nil: nil
neoneye
источник
Этот ответ неверен. Если вы создадите два экземпляра одного и того же класса, он вернет один и тот же адрес памяти для обоих экземпляров. Unmanaged.passUnretained(myObject).toOpaque()вместо этого работает нормально.
Padraig
@Padraig Спасибо, я обновил ваш код. К сожалению, теперь он принимает AnyObjectпараметр. Я бы предпочел Anyв качестве типа ввода.
neoneye
6

В Swift4 про массив:

    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)
    }
SenKe
источник
1
спас мой день Должен быть значок «Самая полезная из последних так приобретенных»
Антон Тропашко
Действительно, это единственный подход, который напечатал мой self?.array.
Ростислав
4

Другие ответы хороши, хотя я искал способ получить адрес указателя в виде целого числа:

let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)

/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int

Просто небольшое продолжение.

Чарли Монро
источник
Ниже приведена версия этого ответа для Swift 3.
RenniePet
3

Ответ @Drew предоставляет только для типа класса.
Ответ @nschum предоставить может быть только для типа структуры.

Однако, если вы используете второй метод, чтобы получить адрес массива с элементом типа value. Swift скопирует весь массив, потому что в Swift массив является копируемым при записи, и Swift не может гарантировать, что он будет вести себя таким образом, как только передаст управление C / C ++ (который запускается с помощью &получения адреса). И если вместо этого вы используете первый метод, он автоматически преобразуется Arrayв то, NSArrayчто, безусловно, нам не нужно.

Итак, самый простой и унифицированный способ, который я нашел, это использование инструкции lldb frame variable -L yourVariableName.

Или вы можете объединить свои ответы:

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

Это для Swift 3.

Как @CharlieMonroe, я хотел получить адрес в виде целого числа. В частности, я хотел, чтобы адрес объекта Thread использовался в качестве идентификатора потока в модуле ведения журнала диагностики для ситуаций, когда имя потока не было доступно.

Основываясь на коде Чарли Монро, вот что я придумал до сих пор. Но будьте осторожны, я очень новичок в Swift, это может быть не правильно ...

  // 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

Последнее утверждение есть, потому что без него я получал адреса типа 0x60000007DB3F. Операция по модулю в последнем утверждении преобразует это в 0x7DB3F.

RenniePet
источник
3

Мое решение на Swift 3

extension MyClass: CustomStringConvertible {
    var description: String {
        return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
    }
}

этот код создает описание как описание по умолчанию <MyClass: 0x610000223340>

Евгений ильин
источник
2

Это, конечно, не самый быстрый и безопасный способ сделать это. Но это работает для меня. Это позволит любому подклассу nsobject принять это свойство.

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
Чарльтон Проватас
источник
спасибо за отзыв Джефф, я буду обновлять ответ
Чарльтон Проватас
1
Неважно! Это была моя ошибка! Ваш код отлично работает с чистым классом Swift. Извините за ошибку.
Джефф