Swift 设置委托会生成编译错误

Swift 设置委托会生成编译错误,swift,delegates,Swift,Delegates,我想使用策略模式来注册一组实现协议的对象。当我设置该协议时,在尝试设置作为协议一部分的委托时,会出现编译错误 出于讨论的目的,我稍微修改了Swift电子书代表团一章中的骰子游戏。重要的变化是: 协议骰子游戏-声明一个委托 类SnakesAndLadders实现DiceGame(因此协议和委托) 类游戏包含3个蛇和梯子的实例 1) 一类具体的蛇和梯子 2) 博弈中的一个let常数 3) 协议博弈的一个“var”变量 如果我们使用具体的类(snakesAndLadders),我们可以设置委托。但

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

出于讨论的目的,我稍微修改了Swift电子书代表团一章中的骰子游戏。重要的变化是:

  • 协议骰子游戏-声明一个委托
  • 类SnakesAndLadders实现DiceGame(因此协议和委托)
  • 类游戏包含3个蛇和梯子的实例 1) 一类具体的蛇和梯子 2) 博弈中的一个let常数 3) 协议博弈的一个“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")
    }
}

问题是,当您将某个内容存储为
协议
时,即使它是一个类,swift也会将其视为
类型,而不是您期望的
引用
类型。因此,不允许对其任何部分进行更改。查看更多信息。

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

但是,如果您将
:class
关键字添加到
DiceGame
协议中,Swift会将其视为引用类型,允许您变异其实例的成员,而不会变异实例本身。请注意,这将约束协议,使其仅符合类类型

protocol DiceGame: class { ... }
添加上述内容后,将允许不可变的
diceGameAsLet
:s属性发生突变


在此上下文中,值得一提的是,
:class
关键字通常用于约束用作委托的协议(例如,在您的示例中,
DiceGameDelegate
),使其仅符合类类型。有了这一附加约束,委托可以用作委托所有者(例如某些类)仅持有
引用的类型,在对委托的强引用可能创建保留循环的上下文中非常有用


有关详细信息,请参见第二部分。

Wow-答案很好。谢谢你准确地指出了我错过的微妙而重要的东西。协议“DiceGame”可能是一个结构,无法修改,因此编译器可以阻止我。我的目的是让这个协议只由类实现,这样就可以告诉编译器问题已经解决了,现在我可以干净地使用“let”变量,并且仍然修改委托属性。(协议DiceGame:class{…})委托
var委托:DiceGameDelegate?
应该是弱引用吗?添加
protocol DiceGame:class
解决了编译问题,现在可以解释为什么了。@JimLeask是的,我认为这是合适的。
Game
的实例拥有一个
snakesandraddes
实例(例如
snakesandraddes
):这是
Game
实例对
snakesandraddes
实例的有力引用。在
Game
setupDelegateAsClass()
函数中,您将
snakesandgraddes
:s
delegate
属性的委托设置为
Game
本身的实例:这是两个相同实例的另一个强引用,但方向相反;因此,我相信,创造了一个强有力的参照周期。看一看。