Как вывести адрес памяти переменной в Swift? (для отладки)
Проблема: получение реального адреса памяти объекта
Вы столкнулись с классической задачей отладки в Swift. Действительно, простое print() выводит описание объекта (реализуя протокол CustomStringConvertible), а withUnsafePointer(to:) даёт адрес самой переменной на стеке, а не объекта в куче. Для ссылочных типов (классов) переменная хранит лишь указатель на объект в куче — именно его адрес нам и нужен.
Решение для классов (reference types)
Самый простой и надёжный способ — использовать функцию Unmanaged.passUnretained(_:).toOpaque(). Она возвращает сырой указатель (UnsafeMutableRawPointer) на объект в куче:
class Person {
var name: String
init(name: String) { self.name = name }
}
let person1 = Person(name: "Alice")
let person2 = person1
let address1 = Unmanaged.passUnretained(person1 as AnyObject).toOpaque()
let address2 = Unmanaged.passUnretained(person2 as AnyObject).toOpaque()
print("person1 address: \(address1)") // 0x0000600003cba0e0 (пример)
print("person2 address: \(address2)") // 0x0000600003cba0e0 (тот же!)
Вы увидите, что адреса совпадают — это доказывает, что person2 — не копия, а ссылка на тот же объект.
Важные нюансы
- Приведение к AnyObject:
Unmanaged.passUnretainedтребует тип, соответствующийAnyObject. Для классов это не проблема, но если ваш тип — структура, нужно использовать другой подход (см. ниже). - Безопасность:
passUnretainedне увеличивает счётчик ссылок. Это безопасно, если вы уверены, что объект жив на момент вызова. Для отладки — идеально. - Альтернатива с unsafeBitCast:
unsafeBitCast(person1, to: UnsafeRawPointer.self)— работает, но считается менее безопасным и читаемым.
Решение для структур (value types)
Со структурами ситуация иная: они хранятся на стеке (или внутри другого объекта), и у них нет постоянного адреса в куче. Но для отладки можно получить адрес самой переменной на стеке:
struct Point {
var x: Int
var y: Int
}
var point = Point(x: 10, y: 20)
withUnsafePointer(to: &point) { ptr in
print("Point address on stack: \(ptr)") // Адрес на стеке
}
Обратите внимание: каждый раз, когда вы передаёте структуру в функцию или присваиваете новой переменной, создаётся копия. Поэтому адреса будут разными.
Удобное расширение для отладки
Чтобы не писать каждый раз громоздкий код, создайте расширение:
extension NSObject {
var memoryAddress: String {
return "\(Unmanaged.passUnretained(self).toOpaque())"
}
}
// Использование:
class Person: NSObject {
var name: String
init(name: String) {
self.name = name
super.init()
}
}
let p = Person(name: "Bob")
print(p.memoryAddress) // 0x0000600001cba2a0
Для классов, не наследующих NSObject, можно сделать аналогичное расширение для AnyObject:
extension AnyObject {
var memoryAddress: String {
return "\(Unmanaged.passUnretained(self).toOpaque())"
}
}
Итог
Для отладки ссылочных типов используйте Unmanaged.passUnretained(...).toOpaque(). Для value types — withUnsafePointer(to:). Помните, что адрес структуры на стеке может меняться при оптимизации компилятора, поэтому полагаться на него в production-коде не стоит.
Альтернативный подход: использование lldb и точек останова
Если вам не нужно выводить адрес в коде, а достаточно посмотреть его во время отладки, проще всего использовать встроенные возможности Xcode и lldb.
Поставьте точку останова (breakpoint) на строке после создания объекта. В консоли отладчика (lldb) введите:
frame variable -L person1
Или сокращённо:
v -L person1
Вы увидите что-то вроде:
0x0000600003cba0e0: (App.Person) person1 = ...
Первое число — это и есть адрес объекта в куче. Для второго объекта person2 адрес будет таким же, если это ссылка.
Преимущества этого подхода
- Не нужно менять код — идеально для быстрой проверки.
- Работает с любыми типами, включая замыкания и метатипы.
- Позволяет сразу посмотреть содержимое объекта по адресу командой
memory read.
Недостатки
- Не подходит, если нужно логировать адрес в runtime (например, в лог-файл).
- Требует запуска через Xcode или использования lldb в консоли.
Этот способ особенно полезен, когда вы отлаживаете утечки памяти или проверяете, не создаётся ли лишняя копия объекта.
Ответов пока нет.
Войдите чтобы оставить ответ