Có cách nào để mô phỏng [NSString stringWithFormat:@"%p", myVar]
, từ Objective-C, trong ngôn ngữ Swift mới không?
Ví dụ:
let str = "A String"
println(" str value \(str) has address: ?")
Có cách nào để mô phỏng [NSString stringWithFormat:@"%p", myVar]
, từ Objective-C, trong ngôn ngữ Swift mới không?
Ví dụ:
let str = "A String"
println(" str value \(str) has address: ?")
Câu trả lời:
Đây là một phần của thư viện chuẩn : 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
Đối với Swift 3, sử dụng withUnsafePointer
:
var str = "A String"
withUnsafePointer(to: &str) {
print(" str value \(str) has address: \($0)")
}
withUnsafePointer
dẫn đến cannot pass immutable value as inout argument
lỗi.
print(self.description)
bản in <MyViewController: 0x101c1d580>
, chúng tôi sử dụng nó như một tài liệu tham khảo. var mutableSelf = self; withUnsafePointer(to: &mutableSelf) { print(String(format: "%p", $0)) }
in 0x16fde4028
rõ ràng là địa chỉ khác nhau. Ai có thể giải thích tại sao?
0x101c1d580
như mong đợi:print(String(format: "%p", unsafeBitCast(self, to: Int.self)))
UnsafePointer
là một cấu trúc, vì vậy để in địa chỉ mà nó trỏ đến (chứ không phải chính cấu trúc), bạn phải in String(format: "%p", $0.pointee)
!
Lưu ý: Đây là loại tham khảo.
print(Unmanaged.passUnretained(someVar).toOpaque())
In địa chỉ bộ nhớ của someVar. (cảm ơn @Ying)
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
In địa chỉ bộ nhớ của someVar.
print(Unmanaged<AnyObject>.passUnretained(someVar as AnyObject).toOpaque())
Unmanaged
nó có thể được thực hiện như thế này : print(Unmanaged<AnyObject>.fromOpaque(&myStruct).toOpaque())
.
Unmanaged.passUnretained(someVar).toOpaque()
(không cần thông số chung)
debugDescription
vào cuối của chuỗi.
Lưu ý rằng câu trả lời này đã khá cũ. Nhiều phương pháp mà nó mô tả không còn hoạt động. Cụ .core
thể không thể truy cập được nữa.
Tuy nhiên, câu trả lời của @ drew là chính xác và đơn giản:
Đây hiện là một phần của thư viện chuẩn: unsafeAddressOf.
Vì vậy, câu trả lời cho câu hỏi của bạn là:
println(" str value \(str) has address: \(unsafeAddressOf(str))")
Đây là câu trả lời ban đầu được đánh dấu đúng (cho hậu thế / phép lịch sự):
Swift "ẩn" con trỏ, nhưng chúng vẫn tồn tại dưới mui xe. (vì thời gian chạy cần nó và vì lý do tương thích với Objc và C)
Tuy nhiên, có vài điều cần biết, nhưng trước tiên làm thế nào để in địa chỉ bộ nhớ của Chuỗi Swift?
var aString : String = "THIS IS A STRING"
NSLog("%p", aString.core._baseAddress) // _baseAddress is a COpaquePointer
// example printed address 0x100006db0
Thao tác này sẽ in địa chỉ bộ nhớ của chuỗi, nếu bạn mở XCode -> Luồng công việc gỡ lỗi -> Xem bộ nhớ và đi đến địa chỉ được in, bạn sẽ thấy dữ liệu thô của chuỗi. Vì đây là một chuỗi ký tự, đây là một địa chỉ bộ nhớ trong bộ lưu trữ nhị phân (không phải stack hoặc heap).
Tuy nhiên, nếu bạn làm
var aString : String = "THIS IS A STRING" + "This is another String"
NSLog("%p", aString.core._baseAddress)
// example printed address 0x103f30020
Điều này sẽ nằm trên ngăn xếp, bởi vì chuỗi được tạo khi chạy
LƯU Ý: .core._baseAddress không được ghi lại, tôi thấy nó đang tìm kiếm trong trình kiểm tra biến và nó có thể bị ẩn trong tương lai
_baseAddress không có sẵn trên tất cả các loại, ở đây một ví dụ khác với CInt
var testNumber : CInt = 289
takesInt(&testNumber)
Trong trường hợp takesInt
là một hàm C helper như thế này
void takesInt(int *intptr)
{
printf("%p", intptr);
}
Về phía Swift, chức năng này là takesInt(intptr: CMutablePointer<CInt>)
, do đó, nó cần một CMutablePulum tới CInt và bạn có thể có được nó với & varname
Hàm in 0x7fff5fbfed98
, một địa chỉ bộ nhớ này bạn sẽ tìm thấy 289 (theo ký hiệu thập lục phân). Bạn có thể thay đổi nội dung của nó với*intptr = 123456
Bây giờ, một số điều khác để biết.
Chuỗi, trong swift, là một kiểu nguyên thủy, không phải là một đối tượng.
CInt là một loại Swift được ánh xạ tới C int Type.
Nếu bạn muốn địa chỉ bộ nhớ của một đối tượng, bạn phải làm một cái gì đó khác nhau.
Swift có một số Loại Con trỏ có thể được sử dụng khi tương tác với C và bạn có thể đọc về chúng tại đây: Các loại Con trỏ Swift
Ngoài ra, bạn có thể hiểu thêm về chúng khám phá khai báo của chúng (cmd + nhấp vào loại), để hiểu cách chuyển đổi một loại con trỏ vào một cái khác
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
là một hàm trợ giúp C tôi đã tạo, với cách thực hiện này
void printptr(void ** ptr)
{
printf("%p", *ptr);
}
Một lần nữa, một ví dụ về địa chỉ được in: 0x6000000530b0
và nếu bạn đi qua trình kiểm tra bộ nhớ, bạn sẽ tìm thấy NSString của mình
Một điều bạn có thể làm với các con trỏ trong Swift (điều này thậm chí có thể được thực hiện với các tham số đầu vào)
func playWithPointer (stringa :AutoreleasingUnsafePointer<NSString>)
{
stringa.memory = "String Updated";
}
var testString : NSString = "test string"
println(testString)
playWithPointer(&testString)
println(testString)
Hoặc, tương tác với 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
( Chỉnh sửa: Swift 1.2 hiện bao gồm một chức năng tương tự được gọi là unsafeAddressOf
.)
Trong Mục tiêu-C, điều này sẽ là [NSString stringWithFormat:@"%p", o]
.
o
là một tham chiếu đến ví dụ. Vì vậy, nếu o
được gán cho một biến khác o2
, địa chỉ được trả về o2
sẽ giống nhau.
Điều này không áp dụng cho các cấu trúc (bao gồm String
) và các kiểu nguyên thủy (như Int
), vì các kiểu này sống trực tiếp trên ngăn xếp. Nhưng chúng ta có thể lấy vị trí trên stack.
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
Trong Mục tiêu-C, điều này sẽ là [NSString stringWithFormat:@"%p", &o]
hoặc [NSString stringWithFormat:@"%p", &i]
.
s
là cấu trúc. Vì vậy, nếu s
được gán cho một biến khác s2
, giá trị sẽ được sao chép và địa chỉ được trả về s2
sẽ khác.
Giống như trong Objective-C, có hai địa chỉ khác nhau được liên kết với o
. Đầu tiên là vị trí của đối tượng, thứ hai là vị trí của tham chiếu (hoặc con trỏ) đến đối tượng.
Có, điều này có nghĩa là nội dung của địa chỉ 0x7fff5fbfe658 là số 0x6100000011d0 như trình gỡ lỗi có thể cho chúng tôi biết:
(lldb) x/g 0x7fff5fbfe658
0x7fff5fbfe658: 0x00006100000011d0
Vì vậy, ngoại trừ các chuỗi là cấu trúc, bên trong tất cả các công việc này khá giống như trong (Mục tiêu-) C.
(Hiện tại kể từ 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
*/
( Ý chính )
Trong Swift, chúng ta xử lý các loại giá trị (cấu trúc) hoặc loại tham chiếu (lớp). Khi làm:
let n = 42 // Int is a structure, i.e. value type
Một số bộ nhớ được phân bổ tại địa chỉ X và tại địa chỉ này, chúng ta sẽ tìm thấy giá trị 42. Việc &n
tạo một con trỏ trỏ đến địa chỉ X, do đó &n
cho chúng ta biết n
vị trí của 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
Khi làm:
class C { var foo = 42, bar = 84 }
var c = C()
Bộ nhớ được phân bổ ở hai nơi:
Như đã nói, các lớp là các loại tham chiếu: vì vậy giá trị của c
được đặt tại địa chỉ X, tại đó chúng ta sẽ tìm thấy giá trị của Y. Và tại địa chỉ Y + 16 chúng ta sẽ tìm thấy foo
và tại địa chỉ Y + 24 chúng ta sẽ tìm thấy bar
( tại + 0 và + 8 chúng tôi sẽ tìm thấy dữ liệu loại và số tham chiếu, tôi không thể nói cho bạn biết nhiều hơn về điều này ...).
(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
là 42 (foo) và 0x54
là 84 (bar).
Trong cả hai trường hợp, sử dụng &n
hoặc &c
sẽ cung cấp cho chúng tôi địa chỉ X. Đối với các loại giá trị, đó là những gì chúng tôi muốn, nhưng không dành cho các loại tham chiếu.
Khi làm:
let referencePointer = UnsafeMutablePointer<C>(&c)
Chúng tôi tạo một con trỏ trên tham chiếu, tức là một con trỏ trỏ đến địa chỉ X. Điều tương tự khi sử dụng 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)
Bây giờ chúng ta đã hiểu rõ hơn về những gì diễn ra dưới mui xe và bây giờ chúng ta sẽ tìm thấy địa chỉ Y (đó là địa chỉ chúng ta muốn), chúng ta có thể làm như sau để có được nó:
let addressY = unsafeBitCast(c, to: Int.self)
Đang xác minh:
(lldb) frame variable addressY -f hex
(Int) addressY = 0x0000000101b2fd20
(lldb) frame variable c
(testmem.C) c = 0x0000000101b2fd20 (foo = 42, bar = 84)
Có nhiều cách khác để làm điều này:
let addressY1 = Int(bitPattern: Unmanaged.passUnretained(c).toOpaque())
let addressY2 = withUnsafeMutableBytes(of: &c) { $0.load(as: Int.self) }
toOpaque()
thực sự gọi unsafeBitCast(c, to: UnsafeMutableRawPointer.self)
.
Tôi hy vọng điều này đã giúp ... nó đã làm cho tôi.
MemoryLocation
tạo ra 2 địa chỉ khác nhau.
===
toán tử nhận dạng được sử dụng để kiểm tra 2 đối tượng trỏ đến cùng một tham chiếu.ObjectIdentifier
để lấy địa chỉ bộ nhớ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
Chỉ cần sử dụng này:
print(String(format: "%p", object))
MyClass?
" không phù hợp với CVarArg, bạn có thể làm extension Optional : CVarArg { }
. Mặt khác, điều này dường như in địa chỉ mà không có sự điên rồ "không an toàn" của các câu trả lời khác.
var _cVarArgEncoding: [Int]
trên CVarArg
. Không rõ làm thế nào nên được thực hiện.
Nếu bạn chỉ muốn thấy điều này trong trình gỡ lỗi và không làm gì khác với nó, thì thực sự không cần phải lấy Int
con trỏ. Để có được chuỗi đại diện của địa chỉ của một đối tượng trong bộ nhớ, chỉ cần sử dụng một cái gì đó như thế này:
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)
}
}
Ví dụ sử dụng:
print(self.pointerString, "Doing something...")
// Prints like: 0x7fd190d0f270 Doing something...
Ngoài ra, hãy nhớ rằng bạn có thể chỉ cần in một đối tượng mà không ghi đè lên đối tượngdescription
đó và nó sẽ hiển thị địa chỉ con trỏ của nó bên cạnh văn bản mô tả (nếu là mật mã).
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()
hoạt động đúng thay thế.
AnyObject
tham số. Tôi thích Any
loại đầu vào.
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
.
Các câu trả lời khác đều ổn, mặc dù tôi đang tìm cách lấy địa chỉ con trỏ dưới dạng số nguyên:
let ptr = unsafeAddressOf(obj)
let nullPtr = UnsafePointer<Void>(bitPattern: 0)
/// This gets the address of pointer
let address = nullPtr.distanceTo(ptr) // This is Int
Chỉ cần theo dõi một chút.
Câu trả lời @Drew cung cấp chỉ có thể được sử dụng cho loại lớp.
Câu trả lời @nschum cung cấp chỉ có thể dành cho kiểu cấu trúc.
Tuy nhiên nếu bạn sử dụng phương thức thứ hai để lấy địa chỉ của một mảng có phần tử kiểu giá trị. Swift sẽ sao chép toàn bộ mảng vì trong mảng Swift là copy-on-write và Swift không thể đảm bảo nó hoạt động theo cách này một khi nó chuyển quyền kiểm soát sang C / C ++ (được kích hoạt bằng cách sử dụng &
để lấy địa chỉ). Và nếu bạn sử dụng phương thức đầu tiên thay vào đó, nó sẽ tự động chuyển đổi Array
thành NSArray
điều chắc chắn là điều chúng tôi không muốn.
Vì vậy, cách đơn giản và thống nhất nhất mà tôi tìm thấy là sử dụng hướng dẫn lldb frame variable -L yourVariableName
.
Hoặc bạn có thể kết hợp câu trả lời của họ:
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
}
Đây là cho Swift 3.
Giống như @CharlieMonroe Tôi muốn lấy địa chỉ dưới dạng số nguyên. Cụ thể, tôi muốn địa chỉ của một đối tượng Thread để sử dụng làm ID luồng trong mô-đun ghi nhật ký chẩn đoán, cho các tình huống không có tên luồng.
Dựa trên mã của Charlie Monroe, đây là những gì tôi nghĩ ra cho đến nay. Nhưng hãy cẩn thận, tôi rất mới với Swift, điều này có thể không đúng ...
// 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
Tuyên bố cuối cùng là ở đó bởi vì không có nó, tôi đã nhận được các địa chỉ như 0x60000007DB3F. Hoạt động modulo trong câu lệnh cuối chuyển đổi nó thành 0x7DB3F.
Giải pháp của tôi trên Swift 3
extension MyClass: CustomStringConvertible {
var description: String {
return "<\(type(of: self)): 0x\(String(unsafeBitCast(self, to: Int.self), radix: 16, uppercase: false))>"
}
}
mã này tạo mô tả như mô tả mặc định
<MyClass: 0x610000223340>
Đây chắc chắn không phải là cách nhanh nhất hoặc an toàn nhất để đi về nó. Nhưng nó làm việc cho tôi. Điều này sẽ cho phép bất kỳ lớp con nsobject nào chấp nhận thuộc tính này.
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
phải là một con trỏ. Trong mã Swift của bạn,str
không phải là một con trỏ. Vì vậy, so sánh không áp dụng.