Чи є все-таки змоделювання [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())
Друкує адресу пам'яті деякогоVar. (спасибі @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Друкує адресу пам'яті деякогоVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
це можна зробити так: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(не потрібна загальна специфікація)
debugDescription
його в кінці.
Зауважте, що ця відповідь була досить старою. Багато описуваних ним методів більше не працюють. Конкретно .core
більше не можна отримати доступ.
Однак відповідь @ Draw правильна і проста:
Тепер це частина стандартної бібліотеки: unsafeAddressOf.
Тож відповідь на ваші запитання:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Ось оригінальна відповідь, яка була позначена правильною (для нащадків / ввічливості):
Свіфт "ховає" вказівники, але вони все ще існують під капотом. (оскільки час його виконання потрібен і з міркувань сумісності з Objc та C)
Однак є кілька речей, які потрібно знати, але спочатку як надрукувати адресу пам'яті Swift String?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Це друкує адресу пам'яті рядка, якщо ви відкриєте XCode -> Debug Workflow -> Переглянути пам'ять і перейдете до надрукованої адреси, ви побачите необроблені дані рядка. Оскільки це рядковий літерал, це адреса пам'яті всередині сховища двійкового файлу (не стека або купи).
Однак, якщо ви
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Це буде в стеці, тому що рядок створюється під час виконання
ПРИМІТКА: .core._baseAdress не задокументований, я виявив, що він шукає інспектора змінної, і він може бути прихований у майбутньому
_baseAddress доступний не для всіх типів, ось ще один приклад із CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Де takesInt
така функція помічника С, як ця
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 + натисніть на тип), щоб зрозуміти, як конвертувати тип вказівника на інший
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
( Редагувати: тепер 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
Отже, за винятком струнних структур, всередині цього все значною мірою працює так само, як у (Об'єктивний) С.
(Поточний на 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. Doing &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 (фу) та 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 була використана як ідентифікатор потоку в модулі діагностичного журналу для ситуацій, коли не було назви імені потоку.
Виходячи з коду Чарлі Монро, ось що я придумав поки що. Але будьте обережні, я дуже новачок у Свіфта, це може бути не правильно ...
// 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
не є вказівником. Тому порівняння не застосовується.