Certain core Foundation types create tagged pointer objects on initialization for performance, such that pointer equality is enough to check for object equality. IIRC, NSDate
s can produce such tagged pointers, but they don't appear to in the case of NaN:
let a = NSDate(timeIntervalSince1970: .nan)
let b = NSDate(timeIntervalSince1970: .nan)
print(ObjectIdentifier(a)) // ObjectIdentifier(0x0000600000ee0080)
print(ObjectIdentifier(b)) // ObjectIdentifier(0x0000600000ee0090)
So to figure this out, we can disassemble -[NSDate isEqualToDate:]
; on my M1 machine running macOS 14.4.1, Hopper shows me
-[NSDate isEqualToDate:]:
000000018051e13c cbz x2, loc_18051e198
000000018051e140 pacibsp
000000018051e144 stp d9, d8, [sp, #-0x30]!
000000018051e148 stp x20, x19, [sp, #0x10]
000000018051e14c stp fp, lr, [sp, #0x20]
000000018051e150 add fp, sp, #0x20
000000018051e154 mov x19, x2
000000018051e158 bl _objc_msgSend$timeIntervalSinceReferenceDate ; _objc_msgSend$timeIntervalSinceReferenceDate
000000018051e15c fmov d8, d0
000000018051e160 mov x0, x19
000000018051e164 bl _objc_msgSend$timeIntervalSinceReferenceDate ; _objc_msgSend$timeIntervalSinceReferenceDate
000000018051e168 mov w8, #0x1
000000018051e16c fcmp d0, d0
000000018051e170 cset w9, vs
000000018051e174 fcmp d8, d8
000000018051e178 csel w9, wzr, w9, vc
000000018051e17c fcmp d8, d0
000000018051e180 csel w0, w8, w9, eq
000000018051e184 ldp fp, lr, [sp, #0x20]
000000018051e188 ldp x20, x19, [sp, #0x10]
000000018051e18c ldp d9, d8, [sp], #0x30
000000018051e190 autibsp
000000018051e194 ret
The relevant portion appears to be:
000000018051e16c fcmp d0, d0
000000018051e170 cset w9, vs
000000018051e174 fcmp d8, d8
000000018051e178 csel w9, wzr, w9, vc
000000018051e17c fcmp d8, d0
000000018051e180 csel w0, w8, w9, eq
I'm far from an expert in ARM assembly, but my understanding is that the first two lines effectively evaluate
w9 = isnan(d0) // d0 is [a timeIntervalSinceReferenceDate]
The next then
w9 = !isnan(d8) ? 0 : w9 // d8 is [b timeIntervalSinceReferenceDate]
= isnan(d8) ? w9 : 0
The last two:
w0 = (d0 == d8) ? 1 : w9 // w0 is the return value
= (d0 == d8) || w9
In other words,
w0 = (d0 == d8) || w9
= (d0 == d8) || (isnan(d8) ? isnan(d0) : 0))
= (d0 == d8) || (isnan(d8) && isnan(d0))
i.e.,
- (BOOL)isEqualToDate:(NSDate *)other {
NSTimeInterval i1 = [self timeIntervalSinceReferenceDate];
NSTimeInterval i2 = [other timeIntervalSinceReferenceDate];
return isnan(i1) && isnan(i2) || i1 == i2;
}
So yes, it appears that NSDate
is doing NaN checks to ensure that such dates end up equal. Still, not recommended to create such dates.