Est-il possible de simuler le [NSString stringWithFormat:@"%p", myVar]
, à partir d'Objective-C, dans le nouveau langage Swift?
Par exemple:
let str = "A String"
println(" str value \(str) has address: ?")
Est-il possible de simuler le [NSString stringWithFormat:@"%p", myVar]
, à partir d'Objective-C, dans le nouveau langage Swift?
Par exemple:
let str = "A String"
println(" str value \(str) has address: ?")
Réponses:
Ce fait maintenant partie de la bibliothèque standard: 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
Pour Swift 3, utilisez withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
entraîne une cannot pass immutable value as inout argument
erreur.
print(self.description)
impressions <MyViewController: 0x101c1d580>
, nous l'utilisons comme référence. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
imprime 0x16fde4028
qui est une adresse clairement différente. Quelqu'un peut-il expliquer pourquoi?
0x101c1d580
comme prévu:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
est une structure, donc pour imprimer l'adresse vers laquelle elle pointe (et non la structure elle-même), vous devez imprimer String(format: "%p", $0.pointee)
!
Remarque: Ceci est pour les types de référence.
print(Unmanaged.passUnretained(someVar).toOpaque())
Imprime l'adresse mémoire de someVar. (merci à @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Imprime l'adresse mémoire de someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
cela peut être fait comme ceci: print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(pas besoin de spécification générique)
debugDescription
à la fin de celle-ci.
Notez que cette réponse était assez ancienne. Bon nombre des méthodes qu'il décrit ne fonctionnent plus. Plus précisément, .core
il n'est plus accessible.
Cependant, la réponse de @ drew est correcte et simple:
Cela fait maintenant partie de la bibliothèque standard: unsafeAddressOf.
La réponse à vos questions est donc:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Voici la réponse originale qui a été marquée correcte (pour la postérité / politesse):
Swift "cache" les pointeurs, mais ils existent toujours sous le capot. (parce que le runtime en a besoin, et pour des raisons de compatibilité avec Objc et C)
Il y a peu de choses à savoir cependant, mais d'abord comment imprimer l'adresse mémoire d'une Swift String?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Cela imprime l'adresse mémoire de la chaîne, si vous ouvrez XCode -> Debug Workflow -> Afficher la mémoire et allez à l'adresse imprimée, vous verrez les données brutes de la chaîne. Puisqu'il s'agit d'un littéral de chaîne, il s'agit d'une adresse mémoire à l'intérieur du stockage du binaire (pas de pile ou de tas).
Cependant, si vous faites
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Ce sera sur la pile, car la chaîne est créée au moment de l'exécution
REMARQUE: .core._baseAddress n'est pas documenté, je l'ai trouvé à la recherche dans l'inspecteur de variables, et il peut être caché à l'avenir
_baseAddress n'est pas disponible sur tous les types, voici un autre exemple avec un CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Où takesInt
est une fonction d'assistance C comme celle-ci
void takesInt(int *intptr)
{
printf("%p", intptr);
}
Du côté Swift, cette fonction est takesInt(intptr: CMutablePointer<CInt>)
, donc elle prend un CMutablePointer vers un CInt, et vous pouvez l'obtenir avec & varname
La fonction imprime 0x7fff5fbfed98
, à cette adresse mémoire, vous trouverez 289 (en notation hexadécimale). Vous pouvez modifier son contenu avec*intptr = 123456
Maintenant, quelques autres choses à savoir.
La chaîne, en swift, est un type primitif, pas un objet.
CInt est un type Swift mappé sur le type C int.
Si vous voulez l'adresse mémoire d'un objet, vous devez faire quelque chose de différent.
Swift a quelques types de pointeurs qui peuvent être utilisés lors de l'interaction avec C, et vous pouvez en savoir plus ici: Types de pointeurs Swift
De plus, vous pouvez en savoir plus sur eux en explorant leur déclaration (cmd + cliquez sur le type), pour comprendre comment convertir un type de pointeur vers un autre
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
est une fonction d'assistance C que j'ai créée, avec cette implémentation
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Encore une fois, un exemple d'adresse imprimée:, 0x6000000530b0
et si vous passez par l'inspecteur de mémoire, vous trouverez votre NSString
Une chose que vous pouvez faire avec des pointeurs dans Swift (cela peut même être fait avec des paramètres inout)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Ou, interagir avec 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 inclut désormais une fonction similaire appelée unsafeAddressOf
.)
En Objective-C, ce serait [NSString stringWithFormat:@"%p", o]
.
o
est une référence à l'instance. Donc, si o
est affecté à une autre variable o2
, l'adresse renvoyée pour o2
sera la même.
Cela ne s'applique pas aux structures (y compris String
) et aux types primitifs (comme Int
), car ceux-ci vivent directement sur la pile. Mais nous pouvons récupérer l'emplacement sur la pile.
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
En Objective-C, ce serait [NSString stringWithFormat:@"%p", &o]
ou [NSString stringWithFormat:@"%p", &i]
.
s
est struct. Donc, si s
est affecté à une autre variable s2
, la valeur sera copiée et l'adresse renvoyée pour s2
sera différente.
Comme dans Objective-C, deux adresses différentes sont associées à o
. Le premier est l'emplacement de l'objet, le second est l'emplacement de la référence (ou du pointeur) vers l'objet.
Oui, cela signifie que le contenu de l'adresse 0x7fff5fbfe658 est le numéro 0x6100000011d0 comme le débogueur peut nous le dire:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Donc, à l'exception des chaînes qui sont des structures, en interne, tout fonctionne à peu près de la même manière que dans (Objective-) C.
(À jour à partir de 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
*/
( Gist )
Dans Swift, nous traitons soit des types valeur (structures), soit des types référence (classes). En faisant:
let n = 42 // Int is a structure, i.e. value type
Une partie de la mémoire est allouée à l'adresse X, et à cette adresse nous trouverons la valeur 42. Faire &n
crée un pointeur pointant vers l'adresse X, donc &n
nous indique où n
se trouve.
(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
En faisant:
class C { var foo = 42, bar = 84 }
var c = C()
La mémoire est allouée à deux endroits:
Comme dit, les classes sont des types de référence: donc la valeur de c
est située à l'adresse X, à laquelle nous trouverons la valeur de Y. Et à l'adresse Y + 16 nous trouverons foo
et à l'adresse Y + 24 nous trouverons bar
( à + 0 et + 8, nous trouverons des données de type et des nombres de références, je ne peux pas vous en dire beaucoup plus à ce sujet ...).
(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
vaut 42 (foo) et 0x54
84 (bar).
Dans les deux cas, utiliser &n
ou &c
nous donnera l'adresse X. Pour les types valeur, c'est ce que nous voulons, mais pas pour les types référence.
En faisant:
let referencePointer = UnsafeMutablePointer<C>(&c)
Nous créons un pointeur sur la référence, c'est-à-dire un pointeur qui pointe vers l'adresse X. Même chose lors de l'utilisation 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)
Maintenant que nous avons une meilleure compréhension de ce qui se passe sous le capot, et que nous maintenant qu'à l'adresse X nous trouverons l'adresse Y (qui est celle que nous voulons), nous pouvons faire ce qui suit pour l'obtenir:
let addressY = unsafeBitCast(c, to: Int.self)
Vérification:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Il existe d'autres moyens de procéder:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
appelle réellement unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
J'espère que cela a aidé ... cela m'a aidé 😆.
MemoryLocation
produit 2 adresses différentes.
===
L'opérateur d'identité est utilisé pour vérifier que 2 objets pointent vers la même référence.ObjectIdentifier
pour obtenir l'adresse mémoireclass 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
Utilisez simplement ceci:
print(String(format: "%p", object))
MyClass?
" n'est pas conforme à CVarArg, vous pouvez le faire extension Optional : CVarArg { }
. Sinon, cela semble imprimer des adresses sans toute la folie «non sûre» des autres réponses.
var _cVarArgEncoding: [Int]
on CVarArg
. Pas clair comment cela devrait être mis en œuvre.
Si vous voulez juste voir cela dans le débogueur et ne rien faire d'autre avec lui, il n'est pas nécessaire d'obtenir le Int
pointeur. Pour obtenir la représentation sous forme de chaîne de l'adresse d'un objet en mémoire, utilisez simplement quelque chose comme ceci:
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)
}
}
Exemple d'utilisation:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
De plus, rappelez- vous que vous pouvez simplement imprimer un objet sans le remplacer description
, et il affichera son adresse de pointeur à côté d'un texte plus descriptif (si souvent cryptique).
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()
fonctionne correctement à la place.
AnyObject
paramètre. Je préférerais Any
comme type d'entrée.
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
.
Les autres réponses sont correctes, même si je cherchais un moyen d'obtenir l'adresse du pointeur sous forme d'entier:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Juste un petit suivi.
La réponse fournie par @Drew ne peut être utilisée que pour le type de classe.
La réponse fournie par @nschum ne peut être que pour le type struct.
Cependant, si vous utilisez la deuxième méthode pour obtenir l'adresse d'un tableau avec un élément de type valeur. Swift copiera tout le tableau car dans Swift, le tableau est une copie sur écriture et Swift ne peut pas s'assurer qu'il se comporte de cette façon une fois qu'il passe le contrôle à C / C ++ (qui est déclenché en utilisant &
pour obtenir l'adresse). Et si vous utilisez la première méthode à la place, elle sera automatiquement convertie Array
en NSArray
ce que nous ne voulons sûrement pas.
Donc, le moyen le plus simple et le plus unifié que j'ai trouvé est d'utiliser l'instruction lldb frame variable -L yourVariableName
.
Ou vous pouvez combiner leurs réponses:
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
}
Ceci est pour Swift 3.
Comme @CharlieMonroe, je voulais obtenir l'adresse sous forme d'entier. Plus précisément, je voulais que l'adresse d'un objet Thread soit utilisée comme ID de thread dans un module de journalisation de diagnostic, pour les situations où aucun nom de thread n'était disponible.
Basé sur le code de Charlie Monroe, voici ce que j'ai trouvé jusqu'ici. Mais attention, je suis très nouveau sur Swift, ce n'est peut-être pas correct ...
// 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
La dernière déclaration est là parce que sans elle, j'obtenais des adresses comme 0x60000007DB3F. L'opération modulo dans la dernière instruction convertit cela en 0x7DB3F.
Ma solution sur Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
ce code crée une description comme la description par défaut
<MyClass: 0x610000223340>
Ce n'est certainement pas le moyen le plus rapide ou le plus sûr de s'y prendre. Mais cela fonctionne pour moi. Cela permettra à toute sous-classe nsobject d'adopter cette propriété.
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
doit être un pointeur. Dans votre code Swift, cestr
n'est pas un pointeur. La comparaison ne s'applique donc pas.