The hash value of the elements in Set changed not as expected

WeakObject adopts Hashableprotocol.

public struct WeakObject {
    public weak var object: AnyObject?
    public init(_ object: AnyObject? = nil) {
        self.object = object
    }
}

extension WeakObject:Hashable{
    public static func == (lhs: WeakObject, rhs: WeakObject) -> Bool {
        return lhs.object === rhs.object
    }
    
    public func hash(into hasher: inout Hasher) {
        hasher.combine(self.object?.hash)
    }
}

class C1{
    var c:Int?
}

Then I created a set.

var c1:C1? = C1()
var c2:C1? = C1()

var bn1 = WeakObject(c1)
var bn2 = WeakObject(c2)

var set:Set<WeakObject> = [bn1,bn2] 
// assume
// bn1.hashValue == A
// bn2.hashValue == B
// WeakObject(nil) == N

Then I set nil to property object:

c1 = nil 
c2 = nil
// now
// WeakObject(nil).hashValue == N
// bn1.hashValue == N
// bn2.hashValue == N

But when I remove Element (or use subtract)

set.remove(BNWeakObject(nil))
// I expect the set will be Empty 
// but
print(set.isEmpty)
// false

You shouldn’t change the hash value of an element in a set (or a dictionary) while it is in the set.

3 Likes

This means that the hash value of the elements in the Set cannot be changed indirectly?

Correct

1 Like

Thanks. I expect the set will change accordingly when the elements' hashValue change

Unfortunately that can’t work; the Set stores values efficiently based on their hash value - if the hash was to change, the value would no longer be at the correct position and the Set wouldn’t know how to find it again.

Also, Sets are value types in Swift, so they can’t mutate in the background. What would happen if your Set was stored to a “let” variable?

1 Like

got it. thx.

See also Hashing Weak Variables