我想使用策略模式来注册一组实现协议的对象。当我设置它时,我在尝试设置作为协议一部分的委托时遇到编译错误。
出于讨论目的,我从Swift电子书的代表团章节略微重写了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")
}
}
答案 0 :(得分:6)
DiceGame
是您作为一种类型使用的异构协议; Swift会将此类型视为值类型,因此(就像结构一样),更改其可变属性也会改变协议类型本身的实例。
但是,如果您将: class
关键字添加到DiceGame
协议,Swift会将其视为引用类型,允许您更改其实例的成员,而不是改变实例本身。请注意,这将限制协议为仅适用于类类型。
protocol DiceGame: class { ... }
通过添加上述内容,将允许不可变diceGameAsLet
:s属性的变异。
在这种情况下,值得一提的是: class
关键字通常用于约束用作委托的协议(例如,示例中的DiceGameDelegate
),因为它只适用于类类型。使用此附加约束,委托可以用作委托所有者(例如某个类)仅包含weak
引用的类型,在对委托的强引用可以创建保留周期的上下文中有用。
参见例如this answer的第二部分了解详情。
答案 1 :(得分:3)
问题在于,当您将某些内容存储为Protocol
时,即使它是一个类,swift也会将其视为value
类型,而不是reference
类型期待他们。因此,不允许更改它的任何部分。有关详细信息,请查看this reference。