Swift Set Crash: Duplicate element found in Set. Elements may have been mutated after insertion

Hi All~ I found ELEMENT_TYPE_OF_SET_VIOLATES_HASHABLE_REQUIREMENTS in Swift Native Set implementation. I have a project: SwiftNotificaitonCenter. I use set to store weak object. like this: https://github.com/100mango/SwiftNotificationCenter/blob/master/SwiftNotificationCenter/WeakObjectSet.swift And Someone pointed me that it would crash: https://github.com/100mango/SwiftNotificationCenter/issues/13. So I look up the Swift implementation. Would you mind telling me that could swift's set can handle the situation like this? when the weak object becomes nil, the set can auto handle duplicate element in Set. Or should I use an array rather than set in swift to store weak object? Thanks a lot.

My guess is that the issue is that Set requires hashValue to remain constant, but your implementation will return a different hashValue depending on whether or not the object is still living. This breaks Set invariants. The Same is likely true of Equatable

A better implementation would be:

struct WeakObject<T: AnyObject>: Equatable, Hashable {
    private let identifier: ObjectIdentifier
    weak var object: T?

    init(_ object: T) {
        self.identifier = ObjectIdentifier(object)
        self.object = object
    }
    
    var hashValue: Int {
       self.identifier.hashValue
    }

    static func ==(lhs: WeakObject, rhs: WeakObject) -> Bool {
       return lhs.identifier == rhs.identifier
    }
}

Depending on how you use this though, you should be careful that:

let a: ObjectIdentifier
{
    let x = SomeObject()
    a = ObjectIdentifier(x)
    // x is deallocated
}
let y = SomeObject() // y happens to be allocated at the same memory location as X
let b = ObjectIdentifier(y)
// no guarantees that a != b
1 Like

Thanks. I fix it according to your advice and remove the equal old element when inserting a new element to prevent ObjectIdentifier be reused.

Terms of Service

Privacy Policy

Cookie Policy