Set and protocols in Swift

后端 未结 2 1574
一整个雨季
一整个雨季 2020-12-20 11:58

I would like to initialize a Set with values corresponding to the Hashable protocol and a custom protocol.

I tried :

protocol CustomProtocol: Hasha         


        
相关标签:
2条回答
  • 2020-12-20 12:29

    In Swift 3, one solution is to use the AnyHashable structure.

    For instance, to create a Observers/Observable pattern, we could do :

    protocol Observer {
        func observableDidSomething(_ observable: Observable)
    }
    
    class Observable {
        private var observersSet: Set<AnyHashable> = []
    
        private var observers: [Observer] {
            return observersSet.flatMap { $0 as? Observer }
        }
    
        func add<O>(_ observer: O) where O : Observer, O : Hashable {
            observersSet.insert(observer)
        }
    
        func remove<O>(_ observer: O) where O : Observer, O : Hashable {
            observersSet.remove(observer)
        }
    
        // ...
    
        private func doSomething() {
            // do something ...
            observers.forEach { $0.observableDidSomething(self) }
        }
    } 
    

    Notice that I separate the Hashable protocol from my protocol Observer.

    0 讨论(0)
  • 2020-12-20 12:33

    The immediate reason why you can't do what you want to do is that Hashable is a generic protocol. Thus it — or a protocol that derives from it — cannot be used as a Set's element type. A generic type can used only as a constraint in another generic. You will notice that you can't declare a Set<Hashable> either, even though a set's element type must conform to Hashable.

    The simplest approach is to make, not a set of protocols, but a set of some object type. For example, if S is a struct that conforms to CustomProtocol (because it conforms to Hashable plus whatever else CustomProtocol entails), you can declare a set of S.

    Example:

    protocol CustomProtocol: Hashable {
    
    }
    
    func ==(lhs:S,rhs:S) -> Bool {
        return lhs.name == rhs.name
    }
    
    struct S : CustomProtocol {
        var name : String
        var hashValue : Int { return name.hashValue }
    }
    
    let set = Set<S>()
    

    If the problem you're trying to solve is that you want a collection of mixed types which are nevertheless in some way equatable to one another, then that is the very same problem solved by protocol extensions, as explained by the discussion in the Protocol-Oriented WWDC 2015 video.

    But it would be simpler just to make all your types classes that derive from NSObject. You can still make them adopt some secondary protocol, of course, but the set won't be defined as a set of that protocol but of NSObject.

    0 讨论(0)
提交回复
热议问题