Есть ли способ имитировать [NSString stringWithFormat:@"%p", myVar]
, из Objective-C, в новом языке Swift?
Например:
let str = "A String"
println(" str value \(str) has address: ?")
Есть ли способ имитировать [NSString stringWithFormat:@"%p", myVar]
, из Objective-C, в новом языке Swift?
Например:
let str = "A String"
println(" str value \(str) has address: ?")
Ответы:
Это теперь является частью стандартной библиотеки: 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 используйте withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
приводит к cannot pass immutable value as inout argument
ошибке.
print(self.description)
печать <MyViewController: 0x101c1d580>
, мы используем это как ссылку. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
печатает, 0x16fde4028
что явно другой адрес. Кто-нибудь может объяснить, почему?
0x101c1d580
как ожидалось:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
это структура, поэтому для того, чтобы напечатать адрес, на который она указывает (а не саму структуру), вы должны напечатать String(format: "%p", $0.pointee)
!
Примечание: это для справочных типов.
print(Unmanaged.passUnretained(someVar).toOpaque())
Печатает адрес памяти someVar. (спасибо @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Печатает адрес памяти someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
это можно сделать так: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(не нужно обобщенной спецификации)
debugDescription
его в конец.
Обратите внимание, что этот ответ был довольно старым. Многие из описанных методов больше не работают. Конкретно .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
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)
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()
Память выделяется в двух местах:
Как уже говорилось, классы являются ссылочными типами: поэтому значение 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
0x2a
42 (foo) и 0x54
84 (бар).
В обоих случаях использование &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)
.
Я надеюсь, что это помогло ... это сделало для меня 😆.
MemoryLocation
выдает 2 разных адреса.
===
Оператор идентификации используется для проверки того, что 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
Просто используйте это:
print(String(format: "%p", object))
MyClass?
" не соответствует CVarArg, вы можете это сделать extension Optional : CVarArg { }
. В противном случае это, кажется, печатает адреса без всего "небезопасного" безумия других ответов.
var _cVarArgEncoding: [Int]
по CVarArg
. Не ясно, как это должно быть реализовано.
Если вы просто хотите увидеть это в отладчике и больше ничего не делать с ним, нет необходимости фактически получать 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...
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()
вместо этого работает нормально.
AnyObject
параметр. Я бы предпочел Any
в качестве типа ввода.
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
.
Другие ответы хороши, хотя я искал способ получить адрес указателя в виде целого числа:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Просто небольшое продолжение.
Ответ @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
}
Это для 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.
Мое решение на 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>
Это, конечно, не самый быстрый и безопасный способ сделать это. Но это работает для меня. Это позволит любому подклассу 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
[NSString stringWithFormat:@"%p", myVar]
,myVar
должен быть указатель. В вашем коде Swiftstr
нет указателя. Таким образом, сравнение не относится.