设置委托生成编译错误
我想使用策略模式来注册一组实现协议的对象。当我设置它时,当试图设置作为协议一部分的委托时,出现编译错误。设置委托生成编译错误
出于讨论的目的,我从Swift电子书的代表团章节稍微修改了DiceGame。意义的变化是:
- 协议DiceGame - 声明一个代表
- 类SnakesAndLadders实现DiceGame(以及因此协议和委托)
- 类游戏保持SnakesAndLadders的3个实例为 1)的具体类SnakesAndLadders的 2)“让”恒定协议的DiceGame 3)协议的“VAR”可变DiceGame
我们可以设置代表细如果我们使用具体类(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")
}
}
的问题是,当你存储的东西为Protocol
,哪怕是一类,快捷认为它们是一个value
类型,而不是你期望他们成为reference
类型。因此,它的任何部分都不允许改变。看看this reference了解更多信息。
DiceGame
是一种异构协议,您将其用作类型; Swift将这种类型视为值类型,因此(就像结构一样),更改其可变属性也会突变协议类型本身的实例。
如果,但是,: class
关键字添加到DiceGame
协议,斯威夫特将把它作为一个引用类型,使您可以变异它的实例的成员,没有突变的实例本身。请注意,这将仅通过类类型将协议约束为,其符合。
protocol DiceGame: class { ... }
通过添加上述的,不可改变diceGameAsLet
突变:的属性将被允许。
在这方面,值得一提的是,: class
关键字通常用来约束作为代表(例如,DiceGameDelegate
在你的例子)通过类类型的顺应性只有协议。有了这个额外的约束条件,代表可以用作代表所有者(例如某个类)只能持有weak
引用的类型,在强烈引用委托可以创建保留周期的上下文中很有用。
参见例如详情请参阅this answer的第二部分。
委托'var委托:DiceGameDelegate?'应该是一个弱引用?添加'协议DiceGame:class'已经解决了编译问题,现在它是有道理的。 –
@JimLeask是的,我相信这是适当的。一个'Game'的实例拥有一个'SnakesAndLadders'实例(例如'snakesAndLadders'):这是从'Game'实例到'SnakesAndLadders'实例的强大引用。在'Game'的'setupDelegateAsClass()'函数中,您将'snakesAndLadders':'delegate'属性的委托设置为'Game'本身的_instance:这是另外两个相同实例的强大参考,但是在另一个方向;如此,我相信创造一个强有力的参考循环。看看[这个相关的问答](http://*.com/questions/30056526/)。 – dfri
哇 - 很好的答案。谢谢。你确切地指出了我错过的那个微妙但重要的东西。协议'DiceGame'可能是一个结构,不能修改,所以编译器是正确的阻止我。我的意图是这个协议只能通过类实现,告诉编译器这个问题消失了,我现在可以干净地使用'let'变量,并且仍然修改委托属性。 (协议DiceGame:类{...}) –