Array of protocol type

前端 未结 3 1418
感动是毒
感动是毒 2021-02-15 11:24

I have checked all answers about this problem on stackoverflow, but still can not figure out how to fix this. My model looks like this

protocol Commandable: Eq         


        
3条回答
  •  我寻月下人不归
    2021-02-15 11:45

    What you need to do is to use type erasure, much like AnyHashable does in the Swift Standard Library.

    You can't do:

    var a: [Hashable] = [5, "Yo"]
    // error: protocol 'Hashable' can only be used as a generic constraint because it has Self or associated type requirements
    

    What you have to do is to use the type-erased type AnyHashable:

    var a: [AnyHashable] = [AnyHashable(5), AnyHashable("Yo")]
    a[0].hashValue // => shows 5 in a playground
    

    So your solution would be to first split the protocol in smaller parts and promote Equatable to Hashable (to reuse AnyHashable)

    protocol Conditionable {
        var condition: Condition? { get set }
    }
    
    protocol Executable {
        func execute() -> SKAction
    }
    
    protocol Commandable: Hashable, Executable, Conditionable {}
    

    Then create an AnyCommandable struct, like this:

    struct AnyCommandable: Commandable, Equatable {
        var exeBase: Executable
        var condBase: Conditionable
        var eqBase: AnyHashable
    
        init(_ commandable: T) where T : Equatable {
            self.condBase = commandable
            self.exeBase = commandable
            self.eqBase = AnyHashable(commandable)
        }
    
        var condition: Condition? {
            get {
                return condBase.condition
            }
            set {
                condBase.condition = condition
            }
        }
    
        var hashValue: Int {
            return eqBase.hashValue
        }
    
        func execute() -> SKAction {
            return exeBase.execute()
        }
    
        public static func ==(lhs: AnyCommandable, rhs: AnyCommandable) -> Bool {
            return lhs.eqBase == rhs.eqBase
        }
    }
    

    And then you can use it like this:

    var a = FunctionCommand()
    a.commands = [AnyCommandable(MoveCommand()), AnyCommandable(FunctionCommand())]
    

    And you can easily access properties of commands, because AnyCommandable implements Commandable

    a.commands[0].condition
    

    You need to remember to now add Hashable and Equatable to all your commands. I used those implementations for testing:

    struct MoveCommand: Commandable {
    
        var movingVector: CGVector!
    
        var condition: Condition?
        func execute() -> SKAction {
            return SKAction()
        }
    
        var hashValue: Int {
            return Int(movingVector.dx) * Int(movingVector.dy)
        }
    
        public static func ==(lhs: MoveCommand, rhs: MoveCommand) -> Bool {
            return lhs.movingVector == rhs.movingVector
        }
    }
    
    struct FunctionCommand: Commandable {
        var commands = [AnyCommandable]()
    
        var condition: Condition?
    
        func execute() -> SKAction {
            return SKAction.group(commands.map { $0.execute() })
        }
    
        var hashValue: Int {
            return commands.count
        }
    
        public static func ==(lhs: FunctionCommand, rhs: FunctionCommand) -> Bool {
            return lhs.commands == rhs.commands
        }
    }
    

提交回复
热议问题