Understanding protocol extension dispatching
There’s an important gotcha to keep in mind when defining protocol extensions. If a type redeclares a method or property defined in a protocol extension, but not in the protocol itself, static dispatching comes into play. This means the implementation of the property of method used depends on the type of the variable or constant — not the actual type of the instance.
Suppose you were to define a protocol similar to TeamRecord called WinLoss:
protocol WinLoss {
var wins: Int { get } var losses: Int { get }
}
...and declared the following extension:
extension WinLoss {
var winningPercentage: Double {
return Double(wins) / (Double(wins) + Double(losses))
}
}
...which is adopted by the following type:
struct CricketRecord: WinLoss { var wins: Int
var losses: Int var draws: Int
var winningPercentage: Double {
return Double(wins) / (Double(wins) + Double(losses) + Double(draws))
}
}
Observe what happens when you use the winningPercentage property:
let miamiTuples = CricketRecord(wins: 8, losses: 7, draws: 1) let winLoss: WinLoss = miamiTuples
miamiTuples.winningPercentage // .5 winLoss.winningPercentage // .53 !!!
Even though miamiTuples and winLoss contain the same instance, you see different results. This is because static dispatching chooses an implementation based on the type of the constants: CricketRecord for miamiTuples and WinLoss for winLoss.
If winningPercentage were defined in the WinLoss protocol, the extension wouldn’t add a new member, but instead simply provide a default implementation for a member already declared in the protocol. In this more common case, dynamic dispatching is used and the choice of implementation depends on the actual type of the instance, not the type of the constant or variable.
You’ve already seen dynamic dispatching in action in Chapter 15, as this is the dispatching method used for overridden properties and methods in class hierarchies.