I have this Swift struct:
struct Foo {
let i: Int
}
let f1 = Foo(i: 1)
let f2 = Foo(i: 2)
Then in LLDB, I cast them to __SwiftValue
and print out:
p (f1 as AnyObject)
(__SwiftValue) 0x00000001676332d0 {
baseNSObject@0 = {
isa = __SwiftValue
}
}
p (f2 as AnyObject)
(__SwiftValue) 0x0000000167633300 {
baseNSObject@0 = {
isa = __SwiftValue
}
}
As you can tell, the address of the 2 are different, which makes sense, they are different data in separate memory locations anyways.
However, if I take ObjectIdentifier
, they are equal:
p ObjectIdentifier(f1 as AnyObject)
(ObjectIdentifier) ObjectIdentifier(0x00000001676332d0) {
_value = 0x00000001676332d0
}
p ObjectIdentifier(f2 as AnyObject)
(ObjectIdentifier) ObjectIdentifier(0x00000001676332d0) {
_value = 0x00000001676332d0
}
This weird behavior basically says "equal ObjectIdentifier" doesn't mean "equal address" (===
):
p (f1 as AnyObject === f2 as AnyObject)
(Bool) (_value = 0)
p (ObjectIdentifier(f1 as AnyObject) == ObjectIdentifier(f2 as AnyObject))
(Bool) (_value = 1)
Why is it so? My understanding is that __SwiftValue
is a subclass of NSObject
, so they should have different ObjectIdentifier
s. This is weird.
Update:
I added example code outside of LLDB:
let f1 = Foo(i: 1)
let f2 = Foo(i: 2)
let b1 = (f1 as AnyObject) === (f2 as AnyObject)
let b2 = ObjectIdentifier(f1 as AnyObject) == ObjectIdentifier(f2 as AnyObject)
And got b1 = false and b2 = true in the debugger:
The documentation says:
This unique identifier is only valid for comparisons during the lifetime of the instance.
In the expression:
ObjectIdentifier(f1 as AnyObject) == ObjectIdentifier(f2 as AnyObject)
After the left hand side of ==
produces an ObjectIdentifier
, the Obj-C object created by f1 as AnyObject
is not alive anymore. The ObjectIdentifier
is no longer valid for comparisons, so the result of the ==
comparison is meaningless.
The deinitialisation of f1 as AnyObject
can be seen more clearly if you replace it with your own class:
class InitDeinitPrinter {
init() {
print("init")
}
deinit {
print("deinit")
}
}
ObjectIdentifier(InitDeinitPrinter()) == ObjectIdentifier(InitDeinitPrinter())
// this prints:
// init
// deinit
// init
// deinit
// demonstrating that one class instance is deinitialised before the other instance is initialised
If you first store f1 as AnyObject
and f2 as AnyObject
into local variables, you keep them alive, and you can legitimately compare their object identifiers.
let f1 = Foo(i: 1)
let f2 = Foo(i: 2)
let boxed1 = f1 as AnyObject
let boxed2 = f2 as AnyObject
let b1 = boxed1 === boxed2
let b2 = ObjectIdentifier(boxed1) == ObjectIdentifier(boxed2)
print(b1, b2) // false, false