Swift Protocols
Protocols make blueprint that define requirements that conforming types must implement.
1. Protocols π©βπ»
1. Protocols
Protocol
μ Methods, Properties, κ·Έλ¦¬κ³ νΉμ μμ
μ΄λ κΈ°λ₯μ μꡬμ¬νμ μ μνκΈ°μν blueprint
λ‘, Protocol μ
Class, Structure, Enumeration μ μ±ν(adopt)
λμ΄ μꡬμ¬νμ ꡬννλλ‘ νλ€. κ·Έλ¦¬κ³ Protocol μ λͺ¨λ μꡬμ¬νμ
μΆ©μ‘±
νλ©΄ κ·Έ Type μ ν΄λΉ Protocol μ μ€μ(conform)
νλ€κ³ νννλ€.
2. Protocol Syntax
Syntax
protocol SomeProtocol {
// protocol definition goes here
}
Protocol μ μ μνλ λ°©λ²μ Class, Structure, Enumeration μ μ μνλ λ°©λ²κ³Ό μ μ¬νλ€.
3. Adopt Protocol
Protocol μ μ±ννλ κ² μμ Class μ Inheritance μ μ μ¬νλ€.
struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}
λ¨, Class
μμλ μ£Όμν΄μΌν κ²μ΄ Inheritance
κ° μ’
λ£λ ν Protocol
μ μ±νμ΄ κ°λ₯νλ€.
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}
4. Adopt Protocol vs. Class Inheritance
Β | Protocol | Class |
---|---|---|
Class | O | O |
Structure | O | X |
Enumeration | O | X |
Multiple Inheritance(or Adapt) | O | X |
2. Protocol Requirements π©βπ»
1. Property Requirements
1 ) Syntax
You can define
var
keyword- type
- name
- { get set } or { get }
static
,class
keyword
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
get set
μ λͺ¨λ μ μν κ²½μ° μλμΌλ‘ Constant Stored Properties μ Read-Only Computed Properties λ μ€μνλ κ²μ΄ λΆκ°λ₯νλ€.λ°λ©΄
get
λ§ μ μν κ²½μ° λͺ¨λ μ’ λ₯μ Properties μ λν΄ Protocol μ μ€μν μ μλ€. κ·Έλ¦¬κ³ μ΄κ²μ΄ μ ν¨ν λset
μ΄ μ ν¨ν νμ μ΄λΌλ©΄set
μ μλμΌλ‘ μ ν¨νλ€.
μ¬κΈ°μ μ£Όμν΄μΌ ν κ²μ΄ { get set }
μ μ΄ Protocol μ μ±ννλ Type μ΄ λ°λμ get set
μ λ§μ‘±νλλ‘ κ΅¬νν΄μΌνλ€λ
μλ―Έμ΄κ³ , { get }
μ λ°λμ get
μ λ§μ‘±νλλ‘ κ΅¬νν΄μΌνλ€λ μλ―Έλ€. βgetβ λ§ λ§μ‘±νκ³ βsetβ μ λ§μ‘±νμ§ μλλ‘
Read-Only
μμ κ°μ νλ κ²μ΄ μλλ€.
You cannot define
let
keyword- What type of properties (i.e. stored properties or computed properties)
Protocol μ΄ Properties μꡬμ¬νμ μ μν λλ λ°λμ
var
keyword λ§ μ¬μ©νλ©°, Properties μ μ νμ μ μν μ μλ€.
2 ) Type Properties
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
λν Protocol μ΄ Type Properties λ₯Ό
μ μν λλ λ§μ°¬κ°μ§λ‘ static
keyword λ₯Ό λ°λμ μμ±ν΄μΌνλ€(μ΄ κ·μΉμ Classes μμν΄ κ΅¬νλ λ class
λλ static
keyword λ₯Ό μꡬνλ κ²½μ° λͺ¨λ μ μ©λλ€).
3 ) Examples
single instance property λ§ μꡬνλ λ§€μ° κ°λ¨ν Protocol FullyNamed λ₯Ό μ μνλ€.
protocol FullyNamed {
var fullName: String { get }
}
μ΄λ₯Ό μ±ννλ Structure λ₯Ό νλ λ§λ€μ΄λ³΄μ.
struct Person: FullyNamed {
var fullName: String
}
μ Person μ FullyNamed Protocol μ μλ²½νκ² μ€μνκ³ μλ€.
var john = Person(fullName: "John Park")
print(john.fullName) // John Park
john μ fullName μ βJohn Parkβ μ΄λ€.
john.fullName = "John Kim"
print(john.fullName) // John Kim
μ΄μ john μ fullName μ βJohn Kimβ μ΄λ€. Protocol μμ var fullName: String { get }
λ‘ μ μνμΌλ
μ΄κ²μ get
λ§ λ§μ‘±ν΄μΌνλ€λ μλ―Έκ° μλκ³ get
μ λ§μ‘±ν΄μΌνλ€λ μλ―Έμ΄κ³ , μ΄κ²μ μ±νν Person Structure λ
fullName μ Variable Stored Properties
λ‘ μ μνκΈ° λλ¬Έμ set
μμ μλμΌλ‘ μ ν¨νκ²λλ€. λ°λΌμ set
μμ μ ν¨ν κ²μ΄λ€.
μ΄λ²μλ μ FullyNamed Protocol μ μ±ννλ μ’ λ 볡μ‘ν Class λ₯Ό νλ μ μν΄λ³Έλ€.
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? "\(prefix!) " : "") + name
}
}
μ΄λ²μλ fullName μ Read-Only Computed Properties
λ‘ μ μνκ³ , Protocol μ΄ get set
μ΄ μλ get
λ§
μ μνκΈ° λλ¬Έμ μμ μ΄ Starship μ FullyNamed Protocol μ μλ²½νκ² μ€μνκ³ μλ€.
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
print(ncc1701.fullName) // USS Enterprise
2. Method Requirements
Methods μ λν μꡬμ¬ν μμ Properties μ μ μ¬νλ€.
1 ) Syntax
You can define
- name
- parameter(including variadic parameter)
- return type
static
keyword
protocol SomeProtocol {
func someTypeMethod() -> SomeType
}
You cannot define
- parameter default value
- method
body
2 ) Type Methods
protocol AnotherProtocol {
static func anotherTypeMethod() -> SomeType
}
3 ) Examples
protocol RandomNumberGenerator {
func random() -> Double
}
μ΄λ₯Ό μ±ννλ Class λ₯Ό νλ λ§λ€μ΄λ³΄μ.
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom + a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
}
μ΄ Class λ μ ν ν©λ μμ±κΈ°(linear congruential generator) λ‘ μλ €μ§ μμ¬ λμ(pseudorandom number) μμ±κΈ° μκ³ λ¦¬μ¦μ΄λ€.
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
print("And another one: \(generator.random())")
Here's a random number: 0.23928326474622771
And another one: 0.4782664609053498
3. Mutating Method Requirements
Protocol μμ Methods λ₯Ό mutating
μΌλ‘ μ μνμ λ μ΄ Protocol μ μ±ννλ Type μ΄ Classes
μΈ κ²½μ°λ
Reference Types μ΄λ―λ‘ mutating
keyword λ₯Ό μμ±ν νμκ° μλ€. μ€μ§ Value Types μΈ
Structures
μ Enumerations
μμλ§ μμ±νλ€.
Example
protocol Toggleable {
mutating func toggle()
}
enum OnOffSwitch: Toggleable {
case off, on
mutating func toggle() {
switch self {
case .off: self = .on
case .on: self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
print("light switch is \(lightSwitch) now.")
lightSwitch.toggle()
print("light switch is \(lightSwitch) now.")
lightSwitch.toggle()
print("light switch is \(lightSwitch) now.")
light switch is off now.
light switch is on now.
light switch is off now.
4. Initializer Requirements
Methods μ λν μꡬμ¬ν μμ Properties μ μ μ¬νλ€.
1 ) Syntax
You can define
- parameter
Methods μ μ μ¬νλ€. νμ§λ§ Initializers λ name κ³Ό explicit return type, static μ΄ νμ©λμ§ μκΈ°
λλ¬Έμ λΉμ°ν Protocol μμ λΆκ°λ₯νλ€. μ¦, μ΄λ€ Custom Initializrer
λ₯Ό ꡬνν΄μΌ νλμ§ κ·Έ Type λ§ μ μνλ€.
protocol SomeProtocol {
init(someParameter: SomeType)
}
You cannot define
- parameter default value
- method
body
2 ) Examples
protocol Human {
var name: String { get set }
var age: Int { get set }
init(name: String, age: Int)
}
struct Student: Human {
var name: String
var age: Int
init(name: String = "[Unknown]", age: Int) {
self.name = name
self.age = age
}
func identification() {
print("My name is \(self.name) and I am \(self.age) years old")
}
}
var jamie = Student(name: "Jamie", age: 20)
jamie.identification()
var kate = Student(age: 22)
kate.identification()
My name is Jamie and I am 20 years old
My name is [Unknown] and I am 22 years old
3 ) Class Implementations of Protocol Initializer Requirements
μμμ Structures λ₯Ό μ΄μ©ν μμ λ₯Ό μ΄ν΄λ³΄μλ€. κ·Έλ°λ° Protocol μ Initializers λ₯Ό Classes
μ μ±ννλ €λ©΄
λ°λμ Required Initializers
λ₯Ό μ¬μ©ν΄ μ΄ Class μ Subclasses κ° λ°λμ μ΄λ₯Ό ꡬννλλ‘ κ°μ ν΄μΌνλ€.
class Student: Human {
var name: String
var age: Int
required init(name: String = "[Unknown]", age: Int) {
self.name = name
self.age = age
}
func identification() {
print("My name is \(self.name) and I am \(self.age) years old")
}
}
required
keyword λ₯Ό μμ±νμ§ μμΌλ©΄ compile-time error κ° λ°μνλ€.
νμ§λ§ Classes κ° final
modifier λ₯Ό μ΄μ©ν΄ μ μλλ κ²½μ°, μ΄ Class λ λ μ΄μ
Subclassing
λ μ μκΈ° λλ¬Έμ required
λ₯Ό μμ±ν νμκ° μλ€.
final class Student: Human {
var name: String
var age: Int
init(name: String = "[Unknown]", age: Int) {
self.name = name
self.age = age
}
func identification() {
print("My name is \(self.name) and I am \(self.age) years old")
}
}
4 ) Class Implementations Overriding Designated Initializer by Protocol Initializer Requirements
λ§μ½ μ΄λ€ Subclass
κ° Protocol μ μν΄ Initializers μ ꡬνμ μꡬλ°λ λ°, κ·Έ Initializers μ Type μ΄
Superclass μ Designated Initializer
μΈ κ²½μ° override
keyword μ ν¨κ» μ¬μ©νλ€.
protocol SomeProtocol {
init()
}
class SomeSuperClass {
init() {
// initializer implementation goes here
}
}
class SomeSubClass: SomeSuperClass, SomeProtocol {
// "required" from SomeProtocol conformance; "override" from SomeSuperClass
required override init() {
// initializer implementation goes here
}
}
5 ) Failable Initializer Requirements
Failable Initializers μμ ν΄λΉ Protocol μ μ±νν Types κ° μ΄λ₯Ό μ€μνλλ‘ μ μν μ μλ€.
Failable Initializer Requirements
λ Failable Initializer λλ Nonfailable Initializer μ μν΄ μΆ©μ‘±λ μ μλ€.Nonfailable Initializer Requirements
λ Nonfailable Initializer λλ Implicitly Unwrapped Failable Initializer μ μν΄ μΆ©μ‘±λ μ μλ€.
3. Protocols as Types π©βπ»
1. Protocols as Types
Protocols λ μ체μ μΌλ‘ μ΄λ ν κΈ°λ₯λ ꡬννμ§ μλλ€. κ·ΈλΌμλ λΆκ΅¬νκ³ μ½λμμ Fully Fledged Types
μΌλ‘ μ¬μ©ν μ μλ€.
Types λ‘ Protocols λ₯Ό μ¬μ©νλ κ²μ βthere exists a type T such that T conforms to the protocolβλΌλ
ꡬμ μμ λΉλ‘―λ μ‘΄μ¬ νμ
(Existential Type)
μ΄λΌ νλ€.
μ¦, Protocols μμ First-Class Citizen μΌλ‘ λ€λ£° μ μλ€λ κ²μ μλ―Ένλ€.
- Function, Method, Initializer μ
Parameter Type
λλReturn Type
μΌλ‘ μ¬μ©λ μ μλ€. Constant, Variable, Property μ Type
μΌλ‘ μ¬μ©λ μ μλ€.Array, Dictionary, λλ λ€λ₯Έ Container μ Type
μΌλ‘ μ¬μ©λ μ μλ€.
Protocols μμ
Swift Types
μ΄λ―λ‘ μ΄λ¦μλλ¬Έμλ‘ μμ
νλ€.
Superclass μμ Subclasss λ‘ Downcasting νλ κ²μ²λΌ
Protocol Type
μμ μ΄κ²μ μ€μνλUnderlying Type
μΌλ‘ Downcasting ν μ μλ€.
2. Examples
protocol RandomNumberGenerator {
func random() -> Double
}
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom + a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
}
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
Array(1...5).forEach { _ in print("Random dice roll is \(d6.roll())") }
Random dice roll is 2
Random dice roll is 3
Random dice roll is 5
Random dice roll is 6
Random dice roll is 2
4. Delegation π©βπ»
1. Delegation
Delegation
μ Class λλ Structure κ° μΌλΆ μ±
μμ λ€λ₯Έ Type μ Instance μκ² hand off(or delegate)
ν μ
μλλ‘ νλ Design Pattern
μ΄λ€. μ΄ Design Pattern μ μμλ μ±
μμ΄ μΊ‘μν(encapsulates)
λ Protocol μ μ μνλ κ²μΌλ‘ ꡬνλμ΄μ§λ©°,
λ리μ(delegate)κ° μμλ κΈ°λ₯μ μ 곡νλ κ²μ 보μ₯νλ€. λ°λΌμ Delegation μ νΉμ μμ
μ μλ΅νκ±°λ μΊ‘μνλ μ νμ μ
νμ μμ΄ κΈ°λ₯μ μ 곡νκ³ μ νλλ° μ¬μ©λλ€.
2. Examples
μμμ λ§λ RandomNumberGenerator Protocol, Dice Class λ₯Ό μ΄μ©ν΄ λ€μ λ Protocols λ₯Ό μ μνλ€.
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate: AnyObject {
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
DiceGame Protocol μ μ£Όμ¬μλ₯Ό μ΄μ©ν μ΄λ€ κ²μμκ²λ μ±νλ μ μκ³ , DiceGameDelegate Protocol μ DiceGame μ μ§ν μνλ₯Ό μΆμ νκΈ° μν΄ μ±νλ μ μλ€.
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = Array(repeating: 0, count: finalSquare + 1)
board[3] = 8; board[6] = 11; board[9] = 9; board[10] = 2
board[14] = -10; board[19] = -11; board[22] = -2; board[24] = -8
}
weak 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: // newSquare == finalSqure : finish
break gameLoop
case let newSquare where newSquare > finalSquare: // roll dice again until it euqals finalSquare
continue gameLoop
default: // progress game play
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
Strong Reference Cycles Between Class instances λ₯Ό μλ°©νκΈ° μν΄ delegates λ Week References
λ‘ μ μΈλμλ€.
Class-Only Protocols μμ λ€μ μ΄ν΄λ³΄κ² μ§λ§,
AnyObject
λ₯Ό μμμν€λκ²μΌλ‘ Protocol μClass-Only Protocols
λ‘ marking λλ€. κ·Έλ¦¬κ³ Class-Only Protocols λ₯Ό μ±νν Class λ λ°λμdelegate λ₯Ό Week Reference λ‘ μ μΈ
ν΄μΌνλ€.
μ΄μ DiceGameDelegate Protocol μ μ±νν Game Tracker λ₯Ό λ§λ€μ΄λ³΄μ.
class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(_ game: DiceGame) {
numberOfTurns = 0
if game is SnakesAndLadders {
print("Started a new game of Snakes and Ladders")
}
print("The game is using a \(game.dice.sides)-side dice")
}
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(_ game: DiceGame) {
print("The game lasted for \(numberOfTurns) turns")
}
}
κ²μμ μ§νμμΌ Delegate
λ₯Ό μ΄λ»κ² μμμμΌ μλνλλ‘ νλμ§ μ΄ν΄λ³΄μ.
let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
Started a new game of Snakes and Ladders
The game is using a 6-side dice
Rolled a 2
Rolled a 3
Rolled a 5
Rolled a 6
Rolled a 2
Rolled a 3
Rolled a 5
Rolled a 6
Rolled a 1
Rolled a 3
Rolled a 4
Rolled a 6
Rolled a 1
Rolled a 3
Rolled a 4
Rolled a 5
Rolled a 1
Rolled a 2
Rolled a 4
Rolled a 5
Rolled a 1
Rolled a 2
Rolled a 3
Rolled a 5
Rolled a 6
Rolled a 2
Rolled a 3
Rolled a 5
Rolled a 6
Rolled a 2
The game lasted for 30 turns
5. Adding Protocol Conformance with an Extension π©βπ»
1. Adding Protocol Conformance with an Extension
κΈ°μ‘΄ νμ μ λν΄ μμ€ μ½λμ μ§μ μ κ·Όν μ μλλΌλ μλ‘μ΄ νλ‘ν μ½μ μ±ννκ³ μ€μνλλ‘ ν΄ νμ₯ν μ μλ€. μ΄λ₯Ό μ΄μ©ν΄ κΈ°μ‘΄ νμ μ μλ‘μ΄ Properties, Methods, Subscripts λ₯Ό μΆκ°ν μ μλ€.
μ΄μ μ Swift Extensions μμ extension
keyword λ§ μ΄μ©ν΄ νμ₯μ νλλ° μ΄λ² μ±ν°μμλ extension
μ νμ₯ν λ
Protocol
μ μ±νμμΌ νμ₯νλλ‘ ν΄λ³Έλ€.
protocol TextRepresentable {
var textualDescription: String { get }
}
Dice Class λ₯Ό μ Protocol μ μ΄μ©ν΄ νμ₯ν΄λ³΄μ.
extension Dice: TextRepresentable {
var textualDescription: String {
return "A \(sides)-sided dice"
}
}
let d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d6.textualDescription) // A 6-sided dice
print(d12.textualDescription) // A 12-sided dice
2. Extending Primitive Types using Protocols
μ΄λ²μλ Swift Strings and Characters μ±ν°μμ μ¬μ©ν΄λ³Έ Swift μ λΆνΈν λ¬Έμμ΄ μ κ·Όκ³Ό Extensions - Subscripts μ±ν°μμ νμ₯ν λ μ¬μ©νλ Subscripts λ₯Ό Protocol μ μ΄μ©ν΄ νμ₯ν΄λ³΄μ.
곡ν΅μΌλ‘ μ¬μ©ν Protocol μ νλ μ μνλ€.
protocol easyIndex {
subscript(_ digitIndex: Int) -> Self { get }
}
1 ) μ°μ Extensions - Subscripts λ₯Ό Protocol μ μ΄μ©νλ κ²μΌλ‘ λ°κΏλ³΄μ
extension Int: easyIndex {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
μ Subscript λ κΈ°μ‘΄ μ±ν°μμ μ΄ν΄λ³Έ κ²κ³Ό λ§μ°¬κ°μ§λ‘
10μ§λ²μ n μΉμ λ₯Ό index
λ‘ μ κ·Όνλ€.
3782[0] // 2, 10^0 μ μλ¦Ώμλ 2λ€.
3782[1] // 8, 10^1 μ μλ¦Ώμλ 8μ΄λ€.
3782[2] // 7, 10^2 μ μλ¦Ώμλ 7μ΄λ€.
3782[3] // 3, 10^3 μ μλ¦Ώμλ 3μ΄λ€.
3782[4] // 0, 10^4 μ μλ¦Ώμλ μ‘΄μ¬νμ§ μμΌλ―λ‘ 0μ΄λ€.
2 ) Swift Strings and Characters μμ index λ₯Ό μ΄μ©ν΄ μ κ·Όν μ μλλ‘ λ°κΏλ³΄μ
let greeting = "Guten Tag!"
print(greeting.startIndex) // Index(_rawBits: 15)
print(greeting.index(greeting.startIndex, offsetBy: 3)) // Index(_rawBits: 196871)
μ΄λ―Έ μ΄μ μ±ν°μμ μ΄ν΄λ³΄μμ§λ§ Swift λ λ¬Έμμ΄μ λν index
κ° λ€λ₯Έ μΈμ΄μλ μ’ λ€λ₯΄λ€. λ°λΌμ μ κ·Όν λλ λ©μλλ₯Ό μ¬μ©ν΄ λ€μκ³Ό κ°μ΄
μ κ·Όν΄μΌλ§νλ€.
print(greeting[greeting.startIndex]) // G
print(greeting[greeting.index(greeting.startIndex, offsetBy: 1)]) // u
print(greeting[greeting.index(greeting.startIndex, offsetBy: 2)]) // t
print(greeting[greeting.index(greeting.endIndex, offsetBy: -1)]) // !
λ°λ©΄ TypeScript λ Python λ± λ€λ₯Έ μΈμ΄μμλ λ€μκ³Ό κ°μ΄ μ§κ΄μ μ΄κ³ μ½κ² μ κ·Όμ΄ κ°λ₯νλ€.
const greeting: string = "Guten Tag!"
console.log(greeting[0]) // G
console.log(greeting[1]) // u
console.log(greeting[2]) // t
console.log(greeting[greeting.length - 1]) // !
μ Protocol μ μ΄μ©ν΄ Swift μ String μ νμ₯ν΄λ³΄μ.
extension String: easyIndex {
subscript(digitIndex: Int) -> String {
String(self[self.index(self.startIndex, offsetBy: digitIndex)])
}
}
μ Subscript λ TypeScript μ λμΌνκ²
μμμλΆν° zero-based index
λ‘ μ κ·Όνλ€.
print(greeting[0]) // G
print(greeting[1]) // u
print(greeting[2]) // t
print(greeting[greeting.count - 1]) // !
3. Conditionally Conforming to a Protocol (where)
Generic Type
μ μ€μ§ Generic parameter κ° Protocol μ μ€μνλ κ²½μ°
μ κ°μ νΉμ 쑰건μμλ§ Protocol μ μꡬμ¬νμ
λ§μ‘±ν μ μλ€. λ°λΌμ Generic Type μ νμ₯ν λ where
λ₯Ό μ΄μ©ν΄ constraints
λ₯Ό λμ΄ν΄ 쑰건λΆλ‘ μ€μνλλ‘ λ§λ€μ΄μΌνλ€.
μ΄κ²μ μΆν Generic Where Clauses μμ
μμΈν λ€λ£¬λ€.
Switch Value Binding with Where μμ λ³Έ κ² μ²λΌ 쑰건μ 맀μΉμν¬ λ
where
λ μ£Όλ‘ μΆκ°μ μΈ μ‘°κ±΄μconstraints
λ‘ μΆκ°νκΈ° μν΄ μ¬μ©λλ€.
extension Array: TextRepresentable where Element: TextRepresentable {
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
μ Protocol μ Array μ TextRepresentable Protocol μ μ±ννλ κ²μΌλ‘ νμ₯
νλ€. κ·Έλ¦¬κ³ μ΄κ²μ΄ μλνλ 쑰건μ
Array μ Element κ° TextRepresentable μ μ€μνλ κ²½μ°
λ‘ μ ννλ€.
κ·ΈλμΌλ§ self.map { $0.textualDescription }
μμ μλ¬κ° λ°μνμ§ μκΈ° λλ¬Έμ΄λ€.
let myDice = [d6, d12]
print(myDice.textualDescription) // [A 6-sided dice, A 12-sided dice]
Element κ° TextRepresentable Protocol μ λ°λ₯΄λ Array
μ΄λ―λ‘ Computed Property textualDescription
λ₯Ό
Member λ‘ κ°λλ€.
let myNumber = [1, 2, 4, 6]
let myString = ["A", "C", "F"]
myNumber.textualDescription // Property 'textualDescription' requires that 'Int' conform to 'TextRepresentable'
myString.textualDescription // Property 'textualDescription' requires that 'String' conform to 'TextRepresentable'
Element κ° TextRepresentable Protocol μ λ°λ₯΄μ§ μλ Array
μ΄λ―λ‘ Computed Property textualDescription
λ₯Ό
Member λ‘ κ°μ§ μμ μλ¬κ° λ°μνλ€.
4. Declaring Protocol Adoption with an Extension
Protocol μ μ±νν΄ νμ₯νλ €λ κΈ°λ₯μ΄ μ΄λ―Έ Type μ μ‘΄μ¬
νλ€λ©΄, μ΄λ»κ² ν΄μΌν κΉ? Swift Extensions
μμ μ΄ν΄λ³Έ κ²μ²λΌ Extension μ Overriding μ ν μ μλ€.
νμ§λ§ μ΄ Type μ΄ μ΄λ€ Protocol μ λ§μ‘±ν¨μ λͺ
μμ μΌλ‘ νν
ν΄μΌ ν μ μλ€. μ΄ κ²½μ° extension
μ μ΄μ©ν΄ Protocol μ μ±ννλ,
μλ¬΄λ° κ΅¬νλ νμ§ μμΌλ©΄ λλ€. μ¦, extension μ body λ₯Ό μμ λΉμλλ©΄ λλ€.
μ΄λ―Έ TextRepresentable μ΄ κ΅¬νλμ΄μλ Hamster Structure κ° μλ€.
struct Hamster {
var name: String
var textualDescription: String {
return "A hamster named \(name)"
}
}
let simonTheHamster = Hamster(name: "Simon")
print(simonTheHamster.textualDescription) // A hamster named Simon
let somethingTextRepresentable: TextRepresentable = simonTheHamster // Value of type 'Hamster' does not conform to specified type 'TextRepresentable'
μ΄λ―Έ ν΄λΉ κΈ°λ₯μ΄ κ΅¬νλμ΄μκΈ° λλ¬Έμ μ¬μ© κ°λ₯νμ§λ§, TextRepresentable Protocol μ λ°λ₯΄κ³ μλ κ²μ μλκΈ° λλ¬Έμ μλ¬κ° λ°μνλ€.
λ°λΌμ μ Hamster κ° TextRepresentable Protocol μ λ§μ‘±νλλ‘ λ§λ€μ΄λ³΄μ.
extension Hamster: TextRepresentable {}
let somethingTextRepresentable: TextRepresentable = simonTheHamster
print(somethingTextRepresentable.textualDescription) // A hamster named Simon
Protocol μ μ±ν
νμ§λ§ ꡬνμ νμ§ μκΈ° λλ¬Έμ Overriding
μ΄ λ°μνμ§ μμΌλ―λ‘ μ μμ μΌλ‘ Extension μ΄ κ°λ₯νλ€. κ²°κ³Όμ μΌλ‘
μ΄μ Hamster λ TextRepresentable λ₯Ό λ§μ‘±νλ κ²μ νμΈν μ μλ€.
6. Adopting a Protocol Using a Synthesized Implementation π©βπ»
Swift λ λ§μ Simple Cases μ λν΄ μλμΌλ‘ Equatable
, Hashable
, Comparable
Protocol μ λ§μ‘±νλλ‘
ν μ μλ€. μ΄λ₯Ό Synthesized Implementation(ν¨μ±λ ꡬν)
μ΄λΌ νλ©°, Protocol μꡬμ¬ν ꡬνμ μ§μ ν νμκ° μλ€.
1. Synthesized Implementation of Equatable
Swift λ λ€μ 쑰건μ λ§μ‘±νλ Custom Types μκ² Equatable
μ μ 곡νλ€.
- Structures that have only stored properties & That stored properties conform to the Equatable protocol
- Enumerations that have only associated types & That associated types conform to the Equatable protocol
- Enumerations that have no associated types
μ 쑰건μ λ§μ‘±νλ κ²½μ° ==
, !=
λ₯Ό μ§μ ꡬννμ§ μκ³ Equatabe
Protocol μ μ±νν¨μΌλ‘μ¨
Synthesized Implementation
μ μ 곡ν μ μλ€.
struct Vector3D {
var x = 0.0, y = 0.0, z = 0.0
}
let alpha = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let beta = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if alpha == beta { // Binary operator '==' cannot be applied to two 'Vector3D' operands
print("These two vectors are also equivalent.")
}
==
λΉκ΅λ₯Ό νκΈ° μν νΌμ°μ° ν¨μκ° μ‘΄μ¬νμ§ μλλ€κ³ μλ¬κ° λ°μλλ€. κ·Έλ°λ° μ΄ Structure λ Stored Properties λ§ μ μ₯
νκ³ μμΌλ©°, κ·Έ Stored Properties λ Equatable Protocol μ λ§μ‘±
νλ―λ‘ μ²« λ²μ§Έ 쑰건μ μν΄ Equatable
Protocol μ
μ±ννλ κ² λ§μΌλ‘ μλμΌλ‘ Synthesized Implementation
μ μ 곡ν μ μλ€.
struct Vector3D: Equatable {
var (x, y, z) = (0.0, 0.0, 0.0)
}
let alpha = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let beta = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if alpha == beta {
print("These two vectors are also equivalent.")
}
These two vectors are also equivalent.
2. Synthesized Implementation of Hashable
Swift λ λ€μ 쑰건μ λ§μ‘±νλ Custom Types μκ² Hashable
μ μ 곡νλ€.
- Structures that have only stored properties & That stored properties conform to the Hashable protocol
- Enumerations that have only associated types & That associated types conform to the Hashable protocol
- Enumerations that have no associated types
μ Equatable
κ³Ό κ±°μ λμΌνλ€λ κ²μ μ μ μλ€. μ 쑰건μ λ§μ‘±νλ κ²½μ° hashValue
, hash(into:)
λ₯Ό μ§μ ꡬννμ§ μκ³
Hashable
Protocol μ μ±νν¨μΌλ‘μ¨ Synthesized Implementation
μ μ 곡ν μ μλ€.
μ°Έκ³ λ‘
Hashable
Protocol μEquatable
Protocol μ μ€μνλ€.extension AnyHashable : Equatable {}
λ°λΌμ
Hashable
Protocol μ μ€μνλ κ²½μ°Equatable
Protocol μSynthesized Implementation
μ ν¨κ» μ 곡νλ€.
struct Vector3D: Hashable {
var (x, y, z) = (0.0, 0.0, 0.0)
}
let alpha = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let beta = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let hashAlpha = alpha.hashValue
let hashBeta = beta.hashValue
if alpha == beta {
print("These two vectors are also equivalent.")
}
print(hashAlpha)
print(hashBeta)
These two vectors are also equivalent.
-4042012231002845599
-4042012231002845599
3. Synthesized Implementation of Comparable
Swift λ Raw Values λ₯Ό κ°κ³ μμ§ μμ Enumerations μκ² λ€μ 쑰건μ λ§μ‘±νλ κ²½μ° Comparable
μ
μ 곡νλ€.
- Enumerations that have no Raw Values
- Enumerations that have no Raw Values
& Enumerations that have associated types
& That associated types conform to the Comparable protocol
μ 쑰건μ λ§μ‘±νλ κ²½μ° <
, <=
, >
, >=
operator λ₯Ό μ§μ ꡬννμ§ μκ³ Comparable
Protocol μ μ±νν¨μΌλ‘μ¨
Synthesized Implementation
μ μ 곡ν μ μλ€.
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
}
var levels = [SkillLevel.intermediate,
SkillLevel.beginner,
SkillLevel.expert(stars: 5),
SkillLevel.expert(stars: 3)]
if SkillLevel.intermediate > SkillLevel.beginner {
print("intermediate is higher level than beginner")
}
for level in levels.sorted() {
print(level)
}
intermidiate is higer level than beginner
beginner
intermediate
expert(stars: 3)
expert(stars: 5)
7. Collections of Protocol Types π©βπ»
Protocols as Types μ΄λ―Έ μ΄ν΄λ³΄μλ―μ΄ Protocols μμ First-Class Citizen μΌλ‘ λ€λ£° μ μμΌλ―λ‘ μ΄κ²μ Collections μ μ μ₯νλ κ² μμ κ°λ₯νλ€.
let d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
let simonTheHamster = Hamster(name: "Simon")
let things: [TextRepresentable] = [d6, d12, simonTheHamster]
for thing in things {
print(thing.textualDescription)
}
A 6-sided dice
A 12-sided dice
A hamster named Simon
8. Protocol Inheritance π©βπ»
1. Protocol Inheritance
Protocol μ Classes, Structures, Enumerations μ Adapt
μν€λ κ² λ§κ³ λ Protocol μ΄
λ€λ₯Έ Protocol μ Inheritance
νλ κ² μμ κ°λ₯νλ€.
Multiple Adapt μ΄ κ°λ₯νλ κ²μ²λΌ Multiple Inherit μμ κ°λ₯νλ€.
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// protocol definition goes here
}
2. Examples
SnakesAndLadders μ TextRepresentable Protocol μ μ±ννκ³ λ€μκ³Ό κ°μ΄ κ²μ μ 보λ₯Ό μΆλ ₯ν μ μλ€.
protocol TextRepresentable {
var textualDescription: String { get }
}
extension SnakesAndLadders: TextRepresentable {
var textualDescription: String {
return "A game of Snakes and Ladders with \(finalSquare) squares"
}
}
let game = SnakesAndLadders()
print(game.textualDescription)
A game of Snakes and Ladders with 25 squares
μ΄μ μ΄ TextRepresentable λ₯Ό μμν΄ PrettyTextRepresentable Protocol μ λ§λ€κ³ , μ΄κ²μ ν λ² λ SnakesAndLadders μ νμ₯ν΄λ³΄μ.
protocol PrettyTextRepresentable: TextRepresentable {
var prettyTextualDescription: String { get }
}
extension SnakesAndLadders: PrettyTextRepresentable {
var prettyTextualDescription: String {
var output = textualDescription + ":\n"
for index in 1...finalSquare {
switch board[index] {
case let ladder where ladder > 0:
output += "β² "
case let snake where snake < 0:
output += "βΌ "
default:
output += "β "
}
}
return output
}
}
let game = SnakesAndLadders()
print(game.prettyTextualDescription)
A game of Snakes and Ladders with 25 squares:
β β β² β β β² β β β² β² β β β βΌ β β β β βΌ β β βΌ β βΌ β
9. Class-Only Protocols π©βπ»
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// class-only protocol definition goes here
}
Delegation Examples μμ λ³Έ κ²μ²λΌ, Class μ μ±νλ§ νμ©νλ €λ©΄, AnyObject
λ₯Ό μμμν΄μΌλ‘μ¨
Class-Only Protocols
λ‘ marking λλ€.
Class-Only Protocols λ₯Ό μ±νν Class λ λ°λμ
delegate λ₯Ό Week Reference λ‘ μ μΈ
ν΄μΌνλ€.
Protocol μ μꡬμ¬νμ μ μλ μλμ΄
Value Semantics
κ° μλReference Semantics
μμ κ°μ νκ±°λ μꡬνλ κ²½μ°Class-Only Protocols
λ₯Ό μ¬μ©νλ€.Which one choose Structures or Classes μμ μ νμ
Inheritance
κ΄κ³λ₯Ό μ€κ³ν λ μ²μλΆν°Protocol
μ μ¬μ©νλ κ²μ κΆμ₯νκ³ μλ€. λ°λΌμ Class μλ§ μ±νλμ΄μΌ νλ κΈ°λ₯μ μμ κ΅¬μ‘°λ‘ μ€κ³ν λ Class Inheritance λμClass-Only Protocols
λ₯Ό μ¬μ©ν μ μλ€.
10. Protocol Composition π©βπ»
1. Protocol Composition between Protocols
λμμ μ¬λ¬ Protocols λ₯Ό μ€μνλ κ²½μ°, μ΄κ²μ λ¨μΌ μꡬμ¬νμΌλ‘ κ²°ν©νλ κ²μ΄ μ μ©ν μ μλ€.
Protocol Composition
μ SomeProtocol & Another Protocol
κ³Ό κ°μ΄ &
λ₯Ό μ΄μ©ν΄ κ²°ν©νλ©°, μ΄κ²μ
Temporary Local Protocol
μΈ κ²μ²λΌ μλνλ€.
λ€μμ Named μ Aged Protocols μ λ μꡬμ¬νμ νλλ‘ κ²°ν©νλ€.
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
&
μ μν΄ Named μ Aged Protocols λ κ²°ν©λμ΄ μꡬμ¬νμ ν λ²μ λ§μ‘±νλλ‘ κ΅¬νν μ μλ€.
let birthdayPerson = Person(name: "Harry", age: 11)
wishHappyBirthday(to: birthdayPerson) // Happy birthday, Harry, you're 11!
2. Protocol Composition with Classes
Named Protocol κ³Ό Location Class λ₯Ό μ μνλ€.
protocol Named {
var name: String { get }
}
class Location {
var latitude: Double
var longitude: Double
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
μ΄μ Location μ μμνκ³ Named λ₯Ό μ±ννλ City Class λ₯Ό μ μνλ€.
class City: Location, Named {
var name: String
init(name: String, latitude: Double, longitude: Double) {
self.name = name
super.init(latitude: latitude, longitude: longitude)
}
}
let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3)
μ΄μ City seattle
μ μ΄λ¦κ³Ό μμΉλ₯Ό μΆλ ₯νλ ν¨μλ₯Ό λ§λ€μ΄λ³΄μ.
1 ) Case 1 - Subclass
func whereIs(_ city: City) {
print("\(city.name), latitude: \(city.latitude), longitude: \(city.longitude)")
}
κ°μ₯ κ°λ¨ν λ°©λ²μ΄λ€. μ²μλΆν° Named λ₯Ό μ€μνλ Subclass City
λ₯Ό μ¬μ©νλ κ²μ΄λ€.
2 ) Case 2 - Downcasting
νμ§λ§ City
κ° μλ μμΉ μ 보μ μ΄λ¦μ κ°λ λ€λ₯Έ Subclass Type μ΄ μΆκ°λλ€λ©΄ μ ν¨μλ μ¬μ¬μ©μ ν μ μκ²λλ€.
λ°λΌμ Parameter λ₯Ό Superclass Location
μ λ°λλ‘ ν΄μΌνλ€.
func whereIs(_ location: Location) {
print("\((location as? City)!.name), latitude: \(location.latitude), longitude: \(location.longitude)")
}
Downcating
μ μ΄μ©νλ©΄ Location μ μμνκ³ Named λ₯Ό μ±ννλ, λ€λ₯Έ Subclass Type μ΄ μΆκ°λλλΌλ Switch
μ
as
λ₯Ό μ΄μ©ν Type Casting μ μ΄μ©ν΄ ν¨μλ₯Ό μ¬μ¬μ© ν μ μλ€.
3 ) Protocol Composition with Class
μ κ²½μ°λ ν¨μλ₯Ό μ¬μ¬μ© ν μλ μμ§λ§, Type μ΄ μΆκ°λ λλ§λ€ ν¨μμ ꡬνμ λ§€λ² μμ ν΄μ€μΌνλ λ¬Έμ κ° μλ€.
Protocol Composition
λ μ΄λ¬ν κ²½μ° λμ± μ μ°νκ² λμν μ μλ€.
func whereIs(_ location: Location & Named) {
print("\(location.name), latitude: \(location.latitude), longitude: \(location.longitude)")
}
Location μ μμνκ³ Named λ₯Ό μ±ννλ, λ€λ₯Έ Subclass Type
μ΄ μΆκ°λλλΌλ ν¨μλ μ¬μ¬μ© κ°λ₯νλ©°, ꡬνμ μμ ν νμκ° μλ€.
μ μΈ κ°μ§ λ°©λ² μ€ μ΄λ€ λ°©λ²μ μ¬μ©νλ λ€μ κ²°κ³Όλ₯Ό μ»λλ€. λ€λ§ Protocol Composition
λ₯Ό μ¬μ©νλ κ²μ΄ μ½λλ₯Ό λ μ μ°νκ² λ§λ λ€.
whereIs(seattle) // Seattle, latitude: 47.6, longitude: -122.3
11. Checking for Protocol Conformance π©βπ»
1. Checking for Protocol Conformance
Type Casting μμ μ€λͺ
νλ―μ΄ is
μ as
μ°μ°μλ₯Ό μ¬μ©ν μ μλ€.
- is : Instance κ° Protocol μ μ€μνλ©΄
true
, μλλ©΄false
λ₯Ό λ°ν. - as? : Instance κ° Protocol μ μ€μνλ©΄
Optional<Protocol Type>
, μλλ©΄nil
μ λ°ν. - as! : Instance κ° Protocol μ μ€μνλ©΄
Protocol Type
, μλλ©΄Runtime Error
λ₯Ό trigger.
Protocol μ νλ μ μνμ.
protocol HasArea {
var area: Double { get }
}
μ΄μ μ Protocol μ μ€μνλ κ°λ¨ν Class λ₯Ό νλ μΆκ°ν΄λ³Έλ€.
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
let country = Country(area: 100.0)
country Instance λ₯Ό is
, as?
, as!
μ°μ°μλ₯Ό μ΄μ©ν΄ νμ
μ 체ν¬ν΄λ³Έλ€.
- is
print(country is HasArea) // true
print(country is Int) // false
if country is HasArea {
print("country conforms to HasArea protocol.")
} else {
print("country do not conforms to HasArea protocol.")
}
country conforms to HasArea protocol.
- as?
print(country as? HasArea) // Optional(__lldb_expr_7.Country)
print(country as? Int) // nil
if let country = country as? HasArea {
print(country)
print("country conforms to HasArea protocol.")
} else {
print("country do not conforms to HasArea protocol.")
}
Optional(__lldb_expr_7.Country)
country conforms to HasArea protocol.
- as!
print(country as! HasArea) // __lldb_expr_11.Country
print(country as! Int) // Could not cast value of type '__lldb_expr_11.Country' (0x1025a8720) to 'NSNumber' (0x1b8cd7ff0).
Forced Unwrapping
μΌλ‘ μΈν΄ Type λΆμΌμΉ μ Runtime Error
κ° λ°μνλ€. as!
λ μλ¬κ° λ°μνμ§ μμμ΄ λͺ
νν κ²½μ°μ
Downcasting
μμ μ¬μ©ν΄μΌνλ€. νμΈνλ μ©λλ‘λ μ ν©νμ§ μλ€.
2. Examples
HasArea Protocol μ μ€μνλ Class μ μ€μνμ§ μλ Class λ₯Ό μΆκ°λ‘ μ μνλ€.
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { pi * radius * radius }
init(radius: Double) { self.radius = radius }
}
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
μ΄μ 3κ°μ Classes λ₯Ό νλμ λ°°μ΄μ λ΄μ Type Checking μ μ΄μ©ν΄ μμ νκ² μνμμΌλ³΄μ.
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
objects.forEach {
if let objectWithArea = $0 as? HasArea {
print("Area is \(objectWithArea.area)")
} else {
print("Something that doesn't have an area")
}
}
Area is 12.5663708
Area is 243610.0
Something that doesn't have an area
12. Optional Protocol Requirements π©βπ»
1. Optional Protocol Requirements Syntax
Protocol μ Requirements λ₯Ό μ μν λ Optional μ μ¬μ©ν μ μλ€. μ΄λ₯Ό Optional Requirements
λΌ νλ©°,
μ΄κ²μ λ°λμ ꡬνν΄μΌνλ μ±
μμ κ°μ§ μλλ€.
μ£Όμν΄μΌν κ²μ΄
Optional Requirements λ Objective-C μ μνΈ μ΄μ©(interoperates)
μ μν κ²μΌλ‘, Protocol μ Type μ λ°λμ @objc
λ₯Ό μ΄μ©ν΄ @objc Protocol
λ‘ μ μν΄μΌνλ€.
λν Optional Requirements λ₯Ό μ μ©ν attributes λ λ°λμ @objc
λ₯Ό μ΄μ©ν΄ @objc attribute
λ‘λ§ μ μλ μ μλ€.
λ§μ§λ§μΌλ‘ μ΄κ²μ΄ Optional
μμ λνλ΄κΈ° μν΄ optional
modifier λ ν¨κ» μμ±ν΄μ€μΌνλ€.
Syntax
@objc protocol SomeProtocol {
@objc optional var mustBeSettable: Int { get set }
@objc optional var doesNotNeedToBeSettable: Int { get }
@objc optional func someTypeMethod() -> SomeType
@objc optional init(someParameter: SomeType)
}
μ°Έκ³ λ‘ Protocol μ΄ κ΅¬ν μ무λ₯Ό κ°μ§ μλλ‘ νλ λ°©λ²μ Optional Protocol μΈμλ Protocol Extensions κ° μλ€. λ¬Όλ‘ , Optional Protocols μ μλ λ°©μμ λ€λ₯΄μ§λ§ κΈ°λ³Έ ꡬνμ μ 곡νλ©°, μ¬μ©μ μ μ ꡬνλ κ°λ₯νκ² ν λΏ μλλΌ Class κ° μλ Structure λ Enumeration μμλ μ¬μ©ν μ μλ€λ μ₯μ μ κ°λλ€.
Optional Protocols μ ꡬν μ무 λ©΄μ κ° μ μννκ³ μ£Όμν΄μΌνλμ§ μ μ ν 4. Optional Protocols as Types μμ μκ°νλ€.
λ¨μν Protocol μ μΌλΆλ₯Ό
Optional
λ‘ μ μνλ κ²μ΄ λͺ©μ μ΄λΌλ©΄ λ€μ μ±ν°μΈ Protocol Extensions λ₯Ό μ¬μ©νλ κ²μ΄ μ’μ λμμ΄ λ μ μλ€.
2. Examples
protocol Member {
var name: String { get set }
var age: Int { get set }
optional var address: String { get } // 'optional' can only be applied to members of an @objc protocol
}
optional
μ μ¬μ©νλ €λ©΄ λ°λμ Objective-C μμ interoperates
κ° νμνλ€. λ°λΌμ @objc protocol
μ member κ°
λμ΄μΌνλ―λ‘ Protocol μ μλ₯Ό λ³κ²½ν΄μΌνλ€.
@objc protocol Member {
var name: String { get set }
var age: Int { get set }
optional var address: String { get } // 'optional' requirements are an Objective-C compatibility feature; add '@objc'
}
Protocol μ μλ₯Ό @objc protocol
λ‘ λ³κ²½νμ§λ§ μ¬μ ν μλ¬κ° λ°μνλ€. optional
μ μ¬μ©νλ €λ©΄ κ·Έ member μμ @objc
λ‘
marking λμ΄μΌνλ€.
@objc protocol Member {
var name: String { get set }
var age: Int { get set }
@objc optional var address: String { get }
}
λλμ΄ μ μμ μΌλ‘ μ μλμλ€. μ¦, Swift λ§ μ¬μ©ν΄ μ½λλ₯Ό μμ±νλλΌλ Optional Requirements λ₯Ό μ¬μ©νλ €λ©΄ λ°λμ @objc
λ‘
μ μν΄μΌνλ€.
struct Teacher: Member { // Non-class type 'Teacher' cannot conform to class protocol 'Member'
var name: String
var age: Int
var address: String
}
Objective-C μ μνΈ μ΄μ©νλ€λ κ²μ μ΄κ²μ΄ Class
μ΄μ΄μΌ ν¨μ μλ―Ένλ€.
λ°λΌμ Structure λ‘ μ μν μ μλ€.
class Teacher: Member {
var name: String
var age: Int
var address: String
init(name: String, age: Int, address: String) {
self.name = name
self.age = age
self.address = address
}
}
class Student: Member {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
Teacher λ optional μ ν¬ν¨ν΄ name, age, address λ₯Ό λͺ¨λ member λ‘ κ°λλ€. λ°λ©΄, Student λ optional μ μ μΈνκ³ name, age λ§ member λ‘ κ°λλ€. μ€μ κ°μ²΄κ° μ μμ μΌλ‘ μλλλμ§ νμΈν΄λ³΄μ.
let jamie = Teacher(name: "Jamie", age: 42, address: "μμΈμ κ°λ¨κ΅¬")
let mike = Student(name: "Mike", age: 20)
var MemberList: [Member] = [jamie, mike]
for member in MemberList {
switch member {
case let manager as Teacher:
print("Teacher name is \(manager.name), he(she) is \(manager.age) years old, and lives in \(manager.address).")
case let student as Student:
print("Student name is \(student.name), he(she) is \(student.age) years old.")
default: break
}
}
Teacher name is Jamie, he(she) is 42 years old, and lives in μμΈμ κ°λ¨κ΅¬.
Student name is Mike, he(she) is 20 years old.
3. Optional Members make them Optional Types
μ Examples λ§ λ³΄λ©΄ κ΅μ₯ν μ μ©ν΄ 보μΈλ€. νμ§λ§ Optional Protocols λ₯Ό μ¬μ©νλ κ²μ λ§€μ° μ‘°μ¬ν΄μΌνλ€.
Protocol μ μ§μ μ±ννλ κ² λΏ μλλΌ Protocol μ Type μΌλ‘ μ¬μ©ν μ μμμ μμμ νμΈνλ€. λ°λ‘ μ΄λ Optional Protocols λ₯Ό Types λ‘ μ¬μ©ν λ μ μννμ§ μμ보μ.
Optional Members λ ꡬν μλ¬΄κ° μκΈ° λλ¬Έμ μ΄κ²μ Types λ‘ μ¬μ©ν λ, ν΄λΉνλ Members μ Type μ νμ Optional μ΄λ€(Member protocol μ κ²½μ° address member κ° νμ Optional μ΄λ€).
μ¦, @objc optional var something: Int { get }
μ Type μ Int
κ° μλλΌ Int?
λ€.
λ§μ°¬κ°μ§λ‘ @objc optional func someFunc(num: Int) -> String
μ Type μ (Int) -> String
μ΄ μλλΌ
((Int) -> String)?
μ΄λ€.
for member in MemberList {
userInformation(user: member)
print("")
}
func userInformation(user: Member) {
print(user.name)
print(user.age)
print(user.address as Any)
}
Jamie
42
Optional("μμΈμ κ°λ¨κ΅¬")
Mike
20
nil
- μ μμ μμ Teacher, Student λ switch λ₯Ό ν΅ν΄
Type Casting
μ νκΈ° λλ¬ΈμMember Protocol μ μ±νν Teacher, Student Types
μμ λͺ νν μ μ μλ€. λ°λΌμ Teacher Class λ address λ₯ΌString
Type μ λͺ λ°±ν κ°κ³ μμΌλ―λ‘ Optional μ΄ μλλ€. λν, Student Class λ address λ₯Ό κ°κ³ μμ§ μμμ ννν μ μ μλ€.- νμ§λ§ μ΄λ² μμ λ Member λ₯Ό Type μΌλ‘ μ¬μ©νλ€. μ¦, Member Protocol μ λ°λ₯΄μ§λ§
Optional μ΄κΈ° λλ¬Έμ Class κ° κ΅¬ν νλμ§ μ¬λΆλ₯Ό μ μ μλ€
. κ·Έλ κΈ° λλ¬ΈμOptional("μμΈμ κ°λ¨κ΅¬")
κ° μΆλ ₯λλ κ²μ΄λ€. λ°λΌμ Optional Protocol μ μ±ννλ Classes λ₯Ό μ¬μ©ν λλ Protocols λ₯Ό Type μΌλ‘ μ¬μ©νλ λμ μ μ ν Type μΌλ‘Downcasting
νκ±°λOptional Chaining
μΌλ‘ μ κ·Όν΄μΌνλ€.
4. Optional Protocols as Types
μμμ μ΄ν΄λ³Έ κ²μ²λΌ Optional Protocols λ₯Ό Type μΌλ‘ μ¬μ©ν λλ μ£Όμν΄μΌνλ€. μ΄κ²μ μ’ λ κ·Ήλ¨μ μΈ μΌμ΄μ€λ₯Ό μ΄μ©ν΄ λ κΉκ² μμ보μ.
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
CounterDataSource λ increment λΌλ Optional Method μ fixedIncrement λΌλ Optional Property λ₯Ό
κ°κ³ μμΌλ©°, λ λ€ Optional Members
λ€.
μ¦, Protocol μ μ±ννλλΌλ μλ¬΄λ° κ΅¬νλ νμ§ μμμ κ°λ₯μ±μ΄ μ‘΄μ¬νλ€.
μ΄λ° μꡬμ¬νμ μ€μνλ Class λ₯Ό λ§λλ κ²μ΄ κΈ°μ μ μΌλ‘λ κ°λ₯νμ§λ§, μ’μ λ°©λ²μ μλλ€. μ΄ Protocol μ μ¬μ©νμ§ μκ³ ν΄λΉ μꡬμ¬νμ μ€μνλ Class μ ꡬνμ ν μ μλ€.
μ΄ Protocol μ Class κ° μ§μ μ±ννμ§ λ§κ³ Type μΌλ‘ μ¬μ©ν΄λ³΄μ.
class Counter {
var count = 0
var dataSource: CounterDataSource
func increment() {
if let amount = dataSource.increment?(forCount: count) {
count += amount
} else if let amount = dataSource.fixedIncrement {
count += amount
}
}
init(dataSource: CounterDataSource) {
self.dataSource = dataSource
}
convenience init(count: Int, dataSource: CounterDataSource) {
self.init(dataSource: dataSource)
self.count = count
}
}
κ·Έλ°λ° dataSource κ° Type μΌλ‘ μ¬μ©νλ CounterDataSource Protocol μ λͺ¨λ Members λ₯Ό ꡬννμ§ μμλ λλ―λ‘,
μ€μ μλ¬΄λ° κ΅¬νλ νμ§ μμμ κ°λ₯μ±μ΄ μ‘΄μ¬νλ€. λ°λΌμ CounterDataSource κ° μλ CounterDataSource?
λ₯Ό μ¬μ©νλ κ²μ΄
μ ν©νλ€.
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
increment(forCount:)
νΈμΆμ 보μ. 첫 λ²μ§Έ?
μCounterDataSource? Type
μ΄λ―λ‘ μ¬μ©λμκ³ , λ λ²μ§Έ?
μ increment κ°Optional Member
μ΄λ―λ‘ κ΅¬ν μ¬λΆλ₯Ό μ μ μμ΄ μ¬μ©λμλ€. μ¦, μ΄λ κ²Optional Chaining
μ μ΄μ©ν΄ μ κ·Όν΄μΌ μμ νλ€.- ν¨μμμ if clause μ else clause μμ
let amount
κ° κ°λ₯ν μ΄μ λincrement(forCount:)
μfixedIncrement
λͺ¨λ Optional Types μ΄λ―λ‘Optional Binding
μ΄ κ°λ₯ν κ²μ΄λ€.
Counter λ₯Ό μλμμΌλ³΄μ.
var counter = Counter()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
0
0
0
0
var dataSource: CounterDataSource?
κ° nil
μ΄κΈ° λλ¬Έμ count = 0 μ μν΄ 0μ λ§€λ² 0μ λνλ―λ‘ λͺ¨λ 0μ΄λ€.
dataSource μ ν λΉν CounterDataSource Type μ Class λ₯Ό νλ λ§λ€μ΄λ³΄μ.
class ThreeSource: NSObject, CounterDataSource {
let fixedIncrement = 3
}
μ΄λ²μλ μ΄ Class λ₯Ό var dataSource: CounterDataSource?
μ ν λΉ ν Counter λ₯Ό μλμμΌλ³΄μ.
var counter = Counter()
counter.dataSource = ThreeSource()
for _ in 1...4 {
counter.increment()
print(counter.count)
}
3
6
9
12
μ΄λ²μλ fixedIncrement
κ° μλ increment(forCount:)
λ₯Ό μ΄μ©ν΄ Counter λ₯Ό μλμμΌλ³΄μ.
class TowardsZeroSource: NSObject, CounterDataSource {
func increment(forCount count: Int) -> Int {
if count == 0 {
return 0
} else if count < 0 {
return 1
} else {
return -1
}
}
}
var counter = Counter()
counter.count = -4
counter.dataSource = TowardsZeroSource()
Array(1...5).forEach { _ in
counter.increment()
print(counter.count)
}
-3
-2
-1
0
0
13. Protocol Extensions π©βπ»
1. Protocol Extensions
Protocol μ μ΄κ²μ μ€μνλ Type μ κΈ°λ₯μ μ 곡νκΈ° μν΄ Extensions μ μ΄μ©ν΄
Computed Properties
, Initializers
, Methods
, Subscripts
μ κΈ°λ³Έ ꡬνμ μ ν©μ±μ μ€μνλ Type μ
μΆκ°ν μ μλ€.
μ΄λ Global Function μ μΆκ°νκ±°λ μΆκ°λ Protocol μ±νμΌλ‘ μΈν΄ κ°λ³ Type λ§λ€ μ ν©μ±μ λ€μ μΆκ°νλ λμ
Protocol Extensions
λ₯Ό μ¬μ©ν΄ κΈ°λ₯μ μ 곡ν μ μλ€.
Protocol Extensions
μ΄ κΈ°λ³Έ ꡬνμ λ°λμ μ 곡νκΈ° λλ¬Έμ μ΄ Protocols λ₯Ό μ±ννλ Types λ μ ν©μ±μ λ§μ‘±νκΈ° μν ꡬνμ κ°μλ°μ§ μμΌλ©°, κΈ°λ₯μ ꡬνμ΄ λ³΄μ₯λλ―λ‘ Optional Protocols μ λ€λ₯΄κ² Optional Chaining μμ΄ νΈμΆλ μ μλ€.
μμ¬ λμ(pseudorandom number) μμ±κΈ°λ₯Ό λ€μ λ μ¬λ €λ³΄μ.
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom + a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
}
let generator = LinearCongruentialGenerator()
Array(1...5).forEach { _ in print("Here's a random number: \(generator.random())") }
Here's a random number: 0.23928326474622771
Here's a random number: 0.4782664609053498
Here's a random number: 0.7172496570644719
Here's a random number: 0.956232853223594
Here's a random number: 0.19521604938271606
κ·Έλ°λ° μ΄ μμ¬ λμ μμ±κΈ°λ₯Ό μ΄μ©ν Bool
μ λ°ννλ ν¨μλ₯Ό μΆκ°νκ³ μΆλ€λ©΄ μ΄λ»κ² ν΄μΌν κΉ?
μμμ Protocol μ κΈ°λ₯μ νμ₯νκ³ μ ν λ Protocol Inheritance λ₯Ό μ¬μ©ν΄ λ€μκ³Ό κ°μ΄ κΈ°λ₯μ μΆκ°νλ€.
protocol RandomBoolGenerator: RandomNumberGenerator {
func randomBool() -> Bool
}
extension LinearCongruentialGenerator: RandomBoolGenerator {
func randomBool() -> Bool {
random() > 0.5
}
}
Array(1...5).forEach { _ in print("Here's a random Boolean: \(generator.randomBool())") }
Here's a random Boolean: false
Here's a random Boolean: false
Here's a random Boolean: true
Here's a random Boolean: true
Here's a random Boolean: false
μμμ μ΄μ©ν κ²½μ° μ°λ¦¬λ λ€μκ³Ό κ°μ΄ 3κ°μ§λ₯Ό ꡬνν΄μΌνλ€.
- RandomNumberGenerator λ₯Ό μμν RandomBoolGenerator Protocol μ μ.
- Extension μ μ΄μ©ν΄ LinearCongruentialGenerator Class μ RandomBoolGenerator λ₯Ό μΆκ°λ‘ μ±ν.
- μ±νλ RandomBoolGenerator Protocol μ μ€μνλλ‘ μ μ.
κ·Έλ°λ° LinearCongruentialGenerator Class λ μ΄λ―Έ RandomNumberGenerator Protocol μ μ€μνκ³ μλ€.
λ°λΌμ Protocol Extensions
λ₯Ό μ¬μ©νλ©΄ Protocol μ μ€μνλ Type μ Protocol μ체λ₯Ό νμ₯
ν¨μΌλ‘μ¨ κΈ°λ₯μ
μ½κ² μΆκ°ν μ μλ€.
extension RandomNumberGenerator {
func randomBool() -> Bool {
random() > 0.5
}
}
Array(1...5).forEach { _ in print("Here's a random Boolean: \(generator.randomBool())") }
Here's a random Boolean: false
Here's a random Boolean: false
Here's a random Boolean: true
Here's a random Boolean: true
Here's a random Boolean: false
Protocol μ νμ₯νλ κ²μ΄λΌ ν΄λ Extension μ Protocol μ΄ μλλ―λ‘ κ΅¬νμ λ―Έλ£° μ μλ€.
extension RandomNumberGenerator { func randomBool() -> Bool // Expected '{' in body of function declaration }
λ°λΌμ Protocol μ νμ₯ν¨κ³Ό λμμ ꡬνμ λ°λμ μ 곡ν΄μΌνλ€.
2. Providing Default Implementations
μμμ μ΄ν΄λ³΄μλ―μ΄
protocol RandomNumberGenerator {
func random() -> Double
}
extension RandomNumberGenerator {
func randomBool() -> Bool {
random() > 0.5
}
}
RandomNumberGenerator λ₯Ό νμ₯νκ³ , RandomNumberGenerator λ₯Ό μ±νν΄ λ€μκ³Ό κ°μ΄ LinearCongruentialGenerator μ μ ν©μ±μ μΆκ°νλ©΄
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom + a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
}
μ΄ LinearCongruentialGenerator Class λ νμ₯λ RandomNumberGenerator
μ κΈ°λ³Έ ꡬνμ λ°μ λ€μκ³Ό κ°μ ννμΈ κ²μ²λΌ
μλν κ²μ΄λ€.
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom + a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
func randomBool() -> Bool {
random() > 0.5
}
}
κ·Έλ°λ° μ΄κ²μ ꡬνμ λ³κ²½νκ³ μΆλ€λ©΄ μ΄λ»κ² ν΄μΌν κΉ? Default λ‘ μ 곡λ μ΄ κ΅¬νμ λ€λ₯΄κ² νκ³ μΆλ€λ©΄ μ΄λ»κ² ν΄μΌν κΉ?
λ§μ½ μ΄κ²μ Protocol Extensions κ° μλ Protocols λ‘ μ μνλ€λ©΄ λ§€λ² RandomBoolGenerator Protocol μ μ±νν λ μ ν©μ± ꡬνμ ν΄μΌνλ―λ‘ νμν Type μ λ§κ² ꡬνμ λ³κ²½νλ©΄ λλ€.
extension LinearCongruentialGenerator: RandomBoolGenerator {
func randomBool() -> Bool {
random() > 0.8
}
}
λ°λ©΄ Extensions μ ꡬνμ μλ¬΄κ° μκΈ° λλ¬Έμ κ·Έλ₯ RandomNumberGenerator Protocol μ μ±νν ν Extensions κ° κΈ°λ³Έ ꡬνμ μ 곡νκΈ°λ‘ ν κΈ°λ₯μ μ§μ ꡬννλ©΄ λλ€.
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom + a + c).truncatingRemainder(dividingBy: m))
return lastRandom / m
}
func randomBool() -> Bool {
random() > 0.8
}
}
κ·Έλ¬λ©΄ Extensions μ κΈ°λ³Έ ꡬνμ μ 곡ν λΏ μ΄λ ν ꡬνλ κ°μνμ§ μκΈ° λλ¬Έμ Protocol μ κΈ°λ₯μ μ§μ μ μΌλ‘ μννμ§ μλλ€.
λ°λΌμ randomBool()
μ LinearCongruentialGenerator Class μ ꡬνμ μν΄ Overriding λλ€.
let generator = LinearCongruentialGenerator()
Array(1...5).forEach { _ in print("Here's a random Boolean: \(generator.randomBool())") }
Here's a random Boolean: false
Here's a random Boolean: false
Here's a random Boolean: false
Here's a random Boolean: true
Here's a random Boolean: false
μ΄λ‘μ¨ λ³λμ ꡬν λ³κ²½μ΄ νμνμ§ μμ κ²½μ° RandomBoolGenerator Protocol μ μ±ννλ κ² λ§μΌλ‘ μ°λ¦¬λ
func randomBool() -> Bool {
random() > 0.5
}
λ₯Ό κΈ°λ³Έ ꡬνμΌλ‘ μ¬μ©ν μ μμΌλ©°, νμμ μ΄λ₯Ό μ§μ ꡬνν΄ Overriding μμΌ μ¬μ©ν μ μλ€.
3. Adding Constraints to Protocol Extensions (where)
Conditionally Conforming to a Protocol (where) μμ
μ΄λ―Έ Protocol μ where
λ₯Ό μ΄μ©ν΄ constraints
λ₯Ό μΆκ°νλ κ²μ νμΈνλ€.
extension Array: TextRepresentable where Element: TextRepresentable {
var textualDescription: String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemsAsText.joined(separator: ", ") + "]"
}
}
μ΄λ²μ μ΄λ₯Ό μ’ λ μΌλ°ν μμΌ Collection μ κΈ°λ₯μ μΆκ°
ν΄λ³΄μ. λ¨, μ μμ μΈ λμμ μν΄ Element μ΄ Equatable μ μ ν©
ν
κ²½μ°λ‘ μ ννλλ‘νλ€.
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
μ Protocol μ λͺ¨λ Element κ° Equatable
μ λ§μ‘±νλ Collection μκ² μκΈ° μμ μ λͺ¨λ Element κ° λμΌνμ§λ₯Ό νλ³ ν
Boolean μ λ°ννλ allEqual()
λ©μλλ₯Ό μΆκ°νλ€.
let equalNumbers = [100, 100, 100, 100, 100]
let differentNumbers = [100, 100, 200, 100, 200]
print(equalNumbers.allEqual()) // true
print(differentNumbers.allEqual()) // false
μ μ½λλ Protocol Extensions μ constraints λ₯Ό μ΄μ©ν΄ κΈ°λ₯μ νμ₯νλ κ²μ μ΄λ€μμΌλ‘ νμ©ν μ μλκ° μ€λͺ νκΈ° μν κ²μΌλ‘ μ€μ μμ κ°μ΄ λ¨μν μ½λλ λ°λ‘ ꡬνν νμ μμ΄ Swift κ° μ΄λ―Έ λͺ¨λ κ±Έ μ 곡νκ³ μλ€.
Higher-order Functions
λ₯Ό μ¬μ©νλ©΄ Collection μ λͺ¨λ κ°μ΄ κ°μμ§
λλ μ΄λ€ κ°μ ν¬ν¨νκ³ μλμ§
λ₯Ό μμ½κ² μ²λ¦¬ν μ μλ€.
- Swift λ
allSatisfy
μcontains
λ₯Ό μ΄μ©ν΄ μμ½κ² μ²λ¦¬ν μ μλ€.
print(equalNumbers.allSatisfy { $0 == equalNumbers[0] }) // true
print(differentNumbers.allSatisfy { $0 == differentNumbers[0] }) // false
print(equalNumbers.contains { $0 == 200 }) // false
print(differentNumbers.contains { $0 == 200 }) // true
- TypeScript λ
every
μsome
μ μ΄μ©ν΄ μμ½κ² μ²λ¦¬ν μ μλ€.
const equalNumbers: Array<number> = [100, 100, 100, 100, 100]
const differentNumbers: Array<number> = [100, 100, 200, 100, 200]
console.log(equalNumbers.every(v => v === equalNumbers[0])) // true
console.log(differentNumbers.every(v => v === equalNumbers[0])) // false
console.log(equalNumbers.some(v => v === 200)) // false
console.log(differentNumbers.some(v => v === 200)) // true
Reference
- βProtocols.β The Swift Programming Language Swift 5.7. accessed Feb. 20, 2023, Swift Docs Chapter 21 - Protocols.