I am using the following low level debugging call to print detailed information about memory address location represented by an integer:
func debugPrintAddressInfo(_ address: Int) {
print(String(format: "address : %016lx", address))
let p = UnsafeRawPointer(bitPattern: address)! // check nil later
let mallocSize = malloc_size(p)
var instanceSize = 0
print("malloc_size : \(mallocSize)")
// π€π€π€
if let objectClass = object_getClass2(address) {
instanceSize = class_getInstanceSize(objectClass)
print("instance size : \(instanceSize)")
print("instance ", terminator: "")
var c: AnyClass? = objectClass
while c != nil {
print(" : \(c!)", terminator: "")
c = class_getSuperclass(c!)
}
print()
}
print("bytes : ", terminator: "")
let size = instanceSize != 0 ? instanceSize : mallocSize != 0 ? mallocSize : 16
let bytes = p.assumingMemoryBound(to: UInt8.self)
for i in 0 ..< size {
print(String(format: "%02x ", bytes[i]), terminator: "")
}
print("\n")
}
Note that the call receives Int as an address, the static type information is not available (type(of: ...)
is not applicable). Usage example:
class BaseClass {
var values: (UInt64, UInt64, UInt64) = (0x1111111111111111, 0x2222222222222222, 0x3333333333333333)
}
class SubClass: BaseClass {}
let c = SubClass()
let intRepresentation = unsafeBitCast(c, to: Int.self)
debugPrintAddressInfo(intRepresentation)
Prints:
address : 0000600000c04c00
malloc_size : 48
instance size : 40
instance : SubClass : BaseClass : _TtCs12_SwiftObject
bytes : 68 82 00 00 01 00 00 00 03 00 00 00 00 00 00 00 11 11 11 11 11 11 11 11 22 22 22 22 22 22 22 22 33 33 33 33 33 33 33 33
Note that in the implementation (line ) I am using a small C wrapper call:
// wrapper.c
#import <objc/runtime.h>
Class object_getClass2(long v) {
void* object = (void *)v;
return object_getClass(object);
}
Interestingly obj-c runtime's "object_getClass" works with Swift objects (is this accidentally?)
I have two questions about this implementation:
-
is it ok to use object_getClass on Swift classes (neither marked @objc nor the subclasses of NSObject.) This seems to work in quick tests (like above).
-
How do I get rid of my C wrapper and use "object_getClass" directly from Swift?
These are the failed attempts:
// π€π€π€
if let objectClass = object_getClass(UnsafeRawPointer(bitPattern: address)) {
// prints:
instance : __SwiftValue : NSObject
// π€π€π€
if let objectClass = object_getClass(address) {
// prints:
instance : __NSCFNumber : NSNumber : NSValue : NSObject
//Desired Output (like in the working example above):
instance : SubClass : BaseClass : _TtCs12_SwiftObject