Specifically, how does Swift memory management work with optionals using the delegate pattern?
Being accustomed to writing the delegate pattern in Objective-C, my instinct is to make the delegate weak
. For example, in Objective-C:
@property (weak) id<FooDelegate> delegate;
However, doing this in Swift isn't so straight-forward.
If we have just a normal looking protocol:
protocol FooDelegate {
func doStuff()
}
We cannot declare variables of this type as weak:
weak var delegate: FooDelegate?
Produces the error:
'weak' cannot be applied to non-class type 'FooDelegate'
So we either don't use the keyword weak
, which allows us to use structs
and enums
as delegates, or we change our protocol to the following:
protocol FooDelegate: class {
func doStuff()
}
Which allows us to use weak
, but does not allow us to use structs
or enums
.
If I don't make my protocol a class protocol, and therefore do not use weak
for my variable, I'm creating a retain cycle, correct?
Is there any imaginable reason why any protocol intended to be used as a delegate protocol shouldn't be a class protocol so that variables of this type can be weak
?
I primarily ask, because in the delegation section of the Apple official documentation on Swift protocols, they provide an example of a non-class protocol and a non-weak variable used as the delegate to their class:
protocol DiceGameDelegate {
func gameDidStart(game: DiceGame)
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
Should we take this as a hint that Apple thinks we should be using structs as delegates? Or is this simply a bad example, and realistically, delegate protocols should be declared as class-only protocols so that the delegated object can hold a weak reference to its delegate?