Beware: since == not a member function, it will not give you dynamic dispatch by default, including if you use it together with a common placeholder.
Consider the following code:
class C: NSObject, Equatable { let id: Int init(_ id: Int) { self.id = id } }
Now create two variables of type NSObject and assign them the same values:
let o1: NSObject = c1 let o2: NSObject = c2
Why? Because you call the function func ==(lhs: NSObject, rhs: NSObject) -> Bool , and not func ==(lhs: C, rhs: C) -> Bool . Which overloaded function for selection is not determined dynamically at runtime, depending on what o1 and o2 refer to. It is determined by Swift at compile time based on the types o1 and o2 , which in this case are NSObject .
NSObject == is implemented differently with your peers - it calls lhs.isEqual(rhs) , which is returned if not overridden to check for reference equality (i.e. are two references pointing to the same object). They arent therefore they are not equal.
Why does this happen with BaseCache , but not with TrackingCache ? Since BaseCache defined as a restriction only of NSObject , therefore T has only the capabilities of NSObject - just as you assigned c1 variable of type NSObject , the version of NSObject from == .
TrackingCache on the other hand guarantees T will be at least a Tracking object, therefore version == for tracking. Swift will choose the more "specific" of all the possible overloads - Tracking more specific than the base class, NSObject .
Here is a simpler example: just using common functions:
func f<T: NSObject>(lhs: T, rhs: T) -> Bool { return lhs == rhs } func g<T: C>(lhs: T, rhs: T) -> Bool { return lhs == rhs } f(c1, c2) // false g(c1, c2) // true
If you want to fix this, you can override isEqual :
class C: NSObject, Equatable { ... override func isEqual(object: AnyObject?) -> Bool { return (object as? C)?.id == id } }
This method (having == call to the method of dynamically allocated classes) is also a way to implement this behavior for your classes other than NSObject. Structures, of course, do not have this problem, because they do not support inheritance - a rating of 1 for structures!