设置委托会生成编译错误

Jim*_*ask 5 delegates swift

我想使用策略模式来注册一组实现协议的对象.当我设置它时,我在尝试设置作为协议一部分的委托时遇到编译错误.

出于讨论目的,我从Swift电子书的代表团章节略微重写了DiceGame.意义的变化是:

  • 协议DiceGame - 声明一个委托
  • class SnakesAndLadders实现DiceGame(以及协议和委托)
  • class Games包含3个SnakesAndLadders实例,1)具体的SnakesAndLadders类2)协议DiceGame的'let'常量3)协议DiceGame的'var'变量

如果我们使用具体类(snakesAndLadders),我们可以设置委托.但是,如果我们使用'let'将其作为协议(diceGameAsLet)保存,则会出现编译错误,但如果我们将变量保存为'var'(diceGameAsVar),则会编译.

它很容易解决,但是,委托本身永远不会改变,因此应该保持为"let"常量,因为它只是内部属性发生变化.关于协议以及它们如何工作和应该如何使用,我一定不能理解(可能是微妙但重要的).

class Dice
{
    func roll() -> Int
    {
        return 7 // always win :)
    }
}

protocol DiceGame
{
    // all DiceGames must work with a DiceGameDelegate
    var delegate:DiceGameDelegate? {get set}

    var dice: Dice {get}
    func play()
}

protocol DiceGameDelegate
{
    func gameDidStart( game:DiceGame )
    func gameDidEnd( game:DiceGame )
}

class SnakesAndLadders:DiceGame
{
    var delegate:DiceGameDelegate?
    let dice = Dice()

    func play()
    {
        delegate?.gameDidStart(self)

        playGame()

        delegate?.gameDidEnd(self)
    }

    private func playGame()
    {
        print("Playing the game here...")
    }
}

class Games : DiceGameDelegate
{
    let snakesAndLadders        = SnakesAndLadders()

    // hold the protocol, not the class
    let diceGameAsLet:DiceGame  = SnakesAndLadders()
    var diceGameAsVar:DiceGame  = SnakesAndLadders()


    func setupDelegateAsClass()
    {
        // can assign the delegate if using the class
        snakesAndLadders.delegate = self
    }

    func setupDelegateAsVar()
    {
        // if we use 'var' we can assign the delegate
        diceGameAsVar.delegate = self
    }

    func setupDelegateAsLet()
    {
        // DOES NOT COMPILE - Why?
        //
        // We are not changing the dice game so want to use 'let', but it won't compile
        // we are changing the delegate, which is declared as 'var' inside the protocol
        diceGameAsLet.delegate = self
    }

    // MARK: - DiceGameDelegate
    func gameDidStart( game:DiceGame )
    {
        print("Game Started")
    }
    func gameDidEnd( game:DiceGame )
    {
        print("Game Ended")
    }
}
Run Code Online (Sandbox Code Playgroud)

dfr*_*fri 6

DiceGame是一种异类协议,您将其用作类型; Swift会将此类型视为值类型,因此(就像结构一样),更改其可变属性也会改变协议类型本身的实例.

但是,如果您将: class关键字添加到DiceGame协议中,Swift会将其视为引用类型,允许您变更实例的成员,而不会改变实例本身.请注意,这将制约该协议为适形于通过类类型而已.

protocol DiceGame: class { ... }
Run Code Online (Sandbox Code Playgroud)

通过添加上述内容,diceGameAsLet将允许不可变的s属性的变异.


在这种情况下,值得一提的是,: class关键字通常用于约束用作委托的协议(例如,DiceGameDelegate在您的示例中),仅适用于类类型.使用此附加约束,委托可以用作委托所有者(例如某些类)仅包含weak引用的类型,在对委托的强引用可以创建保留周期的上下文中有用.

有关详细信息,请参阅此答案的第2部分.


ssc*_*ale 3

问题是,当您将某些内容存储为 a 时Protocol,即使它是一个类,Swift 也会认为它们是一种value类型,而不是reference您期望它们的类型。因此,不允许更改其中的任何部分。请查看此参考以获取更多信息。