I have a small number of instances of a custom class stored in a set. I need to check if a certain element is contained in that set. The criteria for a match must be the object's ID, not its content.
For simplification assume a class with an integer var as the only property, and two different instances of that class, both holding the number 1.
Directly comparing those instances should return true, but when a reference to the first is stored in the set, a query if the set contains a reference to the second one should return false.
Therefore I use the ObjectIdentifier of the object to generate the hash function required by the hashable protocol.
It is my understanding that the .contains method of a Swift Set uses the hash value first, and in case of hash collisions the equatable method is used as a fallback.
But in the following code, which can run in a playground, I get randum results:
class MyClass: Hashable {
var number: Int
init(_ number: Int) {
self.number = number
}
static func == (lhs: MyClass, rhs: MyClass) -> Bool {
return lhs.number == rhs.number
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}
var mySet: Set<MyClass> = []
let number1 = MyClass(1)
let secondNumber1 = MyClass(1)
number1 == secondNumber1 // true: integer values are equal, so are the wrapping classes
number1 === secondNumber1 // false: two different instances
mySet.insert(number1)
mySet.contains(number1) // true
mySet.contains(secondNumber1) // should be false but randomly changes between runs
If you run the above code in an XCode Playground and manually restart playground execution this gives different results for the last line on each run. The desired behaviour is to get "false" every time.
So what would be the correct way to achieve the described bahaviour?