Czy w ogóle istnieje możliwość symulacji [NSString stringWithFormat:@"%p", myVar]
, z Objective-C, w nowym języku Swift?
Na przykład:
let str = "A String"
println(" str value \(str) has address: ?")
Czy w ogóle istnieje możliwość symulacji [NSString stringWithFormat:@"%p", myVar]
, z Objective-C, w nowym języku Swift?
Na przykład:
let str = "A String"
println(" str value \(str) has address: ?")
Odpowiedzi:
Jest obecnie częścią standardowej biblioteki: 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
W przypadku Swift 3 użyj withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
skutkuje cannot pass immutable value as inout argument
błędem.
print(self.description)
wydruki <MyViewController: 0x101c1d580>
, używamy go jako odniesienia. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
wydruki, 0x16fde4028
które są wyraźnie innym adresem. Czy ktoś może wyjaśnić, dlaczego?
0x101c1d580
zgodnie z oczekiwaniami:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
jest strukturą, więc aby wydrukować adres, na który wskazuje (a nie samą strukturę), musisz wydrukować String(format: "%p", $0.pointee)
!
Uwaga: dotyczy to typów referencyjnych.
print(Unmanaged.passUnretained(someVar).toOpaque())
Drukuje adres pamięci someVar. (dzięki @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Drukuje adres pamięci someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
można to zrobić tak: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(nie ma potrzeby ogólnej specyfikacji)
debugDescription
na końcu tego.
Zauważ, że ta odpowiedź była dość stara. Wiele metod, które opisuje, już nie działa. W szczególności .core
nie można już uzyskać do nich dostępu.
Jednak odpowiedź @ drew jest poprawna i prosta:
Jest to teraz część biblioteki standardowej: unsafeAddressOf.
Tak więc odpowiedź na Twoje pytania brzmi:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Oto oryginalna odpowiedź, która została oznaczona jako poprawna (dla potomności / grzeczności):
Szybkie „chowa” wskazówki, ale nadal istnieją pod maską. (ponieważ środowisko wykonawcze tego potrzebuje i ze względu na zgodność z Objc i C)
Jest jednak kilka rzeczy, które należy wiedzieć, ale najpierw jak wydrukować adres pamięci Swift String?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Spowoduje to wyświetlenie adresu pamięci ciągu, jeśli otworzysz XCode -> Debug Workflow -> View Memory i przejdziesz do wydrukowanego adresu, zobaczysz nieprzetworzone dane ciągu. Ponieważ jest to literał łańcuchowy, jest to adres pamięci wewnątrz magazynu pliku binarnego (nie stosu ani sterty).
Jednak jeśli to zrobisz
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Będzie to na stosie, ponieważ ciąg jest tworzony w czasie wykonywania
UWAGA: .core._baseAddress nie jest udokumentowany, znalazłem go w inspektorze zmiennych i może być ukryty w przyszłości
_baseAddress nie jest dostępny we wszystkich typach, tutaj inny przykład z CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Gdzie takesInt
jest taka funkcja pomocnicza C.
void takesInt(int *intptr)
{
printf("%p", intptr);
}
Po stronie Swift ta funkcja jest takesInt(intptr: CMutablePointer<CInt>)
więc pobiera CMutablePointer do CInt i możesz ją uzyskać za pomocą & varname
Funkcja drukuje 0x7fff5fbfed98
, a pod tym adresem pamięci znajduje się 289 (w notacji szesnastkowej). Możesz zmienić jego zawartość za pomocą*intptr = 123456
A teraz kilka innych rzeczy, które warto wiedzieć.
String w swift jest typem pierwotnym, a nie obiektem.
CInt jest typem Swift zamapowanym na typ int C.
Jeśli chcesz mieć adres pamięci obiektu, musisz zrobić coś innego.
Swift ma kilka typów wskaźników, których można używać podczas interakcji z C, i możesz o nich przeczytać tutaj: Typy wskaźników Swift
Ponadto możesz dowiedzieć się więcej o nich, badając ich deklarację (cmd + kliknij typ), aby zrozumieć, jak konwertować rodzaj wskaźnika do innego
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
jest funkcją pomocniczą języka C, którą stworzyłem, z tą implementacją
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Ponownie, przykład wydrukowanego adresu: 0x6000000530b0
a jeśli przejdziesz przez inspektora pamięci, znajdziesz swój NSString
Jedna rzecz, którą możesz zrobić ze wskaźnikami w Swift (można to nawet zrobić z parametrami inout)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Lub, wchodząc w interakcję z 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
( Edycja: Swift 1.2 zawiera teraz podobną funkcję o nazwie unsafeAddressOf
.)
W Objective-C tak będzie [NSString stringWithFormat:@"%p", o]
.
o
jest odniesieniem do instancji. Więc jeśli o
zostanie przypisana do innej zmiennej o2
, zwracany adres o2
będzie taki sam.
Nie dotyczy to struktur (w tym String
) i typów pierwotnych (takich jak Int
), ponieważ te żyją bezpośrednio na stosie. Ale możemy pobrać lokalizację na stosie.
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
W Objective-C byłoby to [NSString stringWithFormat:@"%p", &o]
lub [NSString stringWithFormat:@"%p", &i]
.
s
jest strukturą. Jeśli więc s
zostanie przypisana do innej zmiennej s2
, wartość zostanie skopiowana, a zwracany adres dla s2
będzie inny.
Podobnie jak w Objective-C, istnieją dwa różne adresy powiązane z o
. Pierwsza to lokalizacja obiektu, druga to lokalizacja odniesienia (lub wskaźnika) do obiektu.
Tak, to znaczy, że zawartość adresu 0x7fff5fbfe658 to numer 0x6100000011d0, ponieważ debugger może nam powiedzieć:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Tak więc, z wyjątkiem łańcuchów będących strukturami, wewnętrznie to wszystko działa prawie tak samo jak w (Objective-) C.
(Aktualne od 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
*/
( Streszczenie )
W Swift mamy do czynienia z typami wartości (strukturami) lub typami referencyjnymi (klasami). Robiąc:
let n = 42 // Int is a structure, i.e. value type
Część pamięci jest alokowana pod adresem X i pod tym adresem znajdziemy wartość 42. Doing &n
tworzy wskaźnik wskazujący na adres X, a zatem &n
mówi nam, gdzie n
się znajduje.
(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
Robiąc:
class C { var foo = 42, bar = 84 }
var c = C()
Pamięć jest przydzielana w dwóch miejscach:
Jak już powiedziano, klasy są typami referencyjnymi: więc wartość c
znajduje się pod adresem X, pod którym znajdziemy wartość Y. A pod adresem Y + 16 znajdziemy, foo
a pod adresem Y + 24 znajdziemy bar
( na + 0 i + 8 znajdziemy dane typu i liczbę referencji, nie mogę powiedzieć więcej na ten temat ...).
(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
wynosi 42 (foo) i 0x54
84 (bar).
W obu przypadkach użycie &n
lub&c
da nam adres X. W przypadku typów wartości właśnie tego chcemy, ale nie w przypadku typów referencyjnych.
Robiąc:
let referencePointer = UnsafeMutablePointer<C>(&c)
Tworzymy wskaźnik na referencję, tj. Wskaźnik, który wskazuje na adres X. To samo przy używaniu 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)
Teraz, gdy mamy lepsze zrozumienie tego, co dzieje się pod maską, i że teraz pod adresem X znajdziemy adres Y (który jest tym, którego potrzebujemy), możemy wykonać następujące czynności, aby go uzyskać:
let addressY = unsafeBitCast(c, to: Int.self)
Weryfikacja:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Można to zrobić na inne sposoby:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
faktycznie dzwoni unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Mam nadzieję, że to pomogło ... pomogło mi 😆.
MemoryLocation
tworzy 2 różne adresy.
===
Operator tożsamości służy do sprawdzania, czy 2 obiekty wskazują na to samo odniesienie.ObjectIdentifier
aby uzyskać adres pamięciclass 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
Po prostu użyj tego:
print(String(format: "%p", object))
MyClass?
niezgodności „ ” z CVarArg, możesz to zrobić extension Optional : CVarArg { }
. W przeciwnym razie wydaje się, że wypisuje adresy bez „niebezpiecznego” szaleństwa innych odpowiedzi.
var _cVarArgEncoding: [Int]
on CVarArg
. Nie jest jasne, jak należy to wdrożyć.
Jeśli chcesz po prostu zobaczyć to w debugerze i nie robić z tym nic więcej, nie ma potrzeby pobierania Int
wskaźnika. Aby uzyskać ciąg znaków reprezentujący adres obiektu w pamięci, użyj czegoś takiego:
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)
}
}
Przykładowe użycie:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Ponadto pamiętaj, że możesz po prostu wydrukować obiekt bez nadpisywania jego description
, a on pokaże swój adres wskaźnika obok bardziej opisowego (jeśli często tajemniczego) tekstu.
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()
zamiast tego działa prawidłowo.
AnyObject
parametr. Wolałbym Any
jako typ wejścia.
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
.
Pozostałe odpowiedzi są w porządku, chociaż szukałem sposobu na uzyskanie adresu wskaźnika jako liczby całkowitej:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Tylko mała kontynuacja.
Odpowiedzi @Drew można użyć tylko dla typu klasy.
Odpowiedź @nschum może dotyczyć tylko typu struktury.
Jeśli jednak używasz drugiej metody, aby uzyskać adres tablicy z elementem typu wartości. Swift skopiuje całą tablicę, ponieważ w Swift tablica jest kopiowana przy zapisie i Swift nie może upewnić się, że zachowa się w ten sposób po przekazaniu kontroli do C / C ++ (co jest wyzwalane przez użycie &
do uzyskania adresu). A jeśli zamiast tego użyjesz pierwszej metody, zostanie ona automatycznie przekonwertowana Array
na NSArray
coś, czego z pewnością nie chcemy.
Najprostszym i najbardziej ujednoliconym sposobem, jaki znalazłem, jest użycie instrukcji lldb frame variable -L yourVariableName
.
Lub możesz połączyć ich odpowiedzi:
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
}
To jest dla Swift 3.
Podobnie jak @CharlieMonroe, chciałem uzyskać adres jako liczbę całkowitą. W szczególności chciałem, aby adres obiektu Thread był używany jako identyfikator wątku w module rejestrowania diagnostycznego w sytuacjach, w których nazwa wątku nie była dostępna.
W oparciu o kod Charliego Monroe, oto co wymyśliłem do tej pory. Ale uwaga, jestem nowy w Swift, to może nie być poprawne ...
// 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
Ostatnia instrukcja jest tam, ponieważ bez niej otrzymywałem adresy takie jak 0x60000007DB3F. Operacja modulo w ostatniej instrukcji konwertuje to na 0x7DB3F.
Moje rozwiązanie w Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
ten kod tworzy opis jak domyślny opis
<MyClass: 0x610000223340>
Z pewnością nie jest to najszybszy ani najbezpieczniejszy sposób. Ale to działa dla mnie. Pozwoli to każdej podklasie nsobject na przyjęcie tej właściwości.
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
musi być wskaźnikiem. W kodzie Swiftstr
nie jest wskaźnikiem. Więc porównanie nie ma zastosowania.