Чи є все-таки змоделювання [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 (фу) та 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).
Я сподіваюся, що це допомогло ... це зробило для мене 😆.
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не є вказівником. Тому порівняння не застосовується.