정의
- 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진
- 프로토콜을 만족시키는 타입을 채택(conform)이라고 합니다.
- 프로토콜에 필수 구현을 추가하거나 추가적인 기능을 더하기 위해 프로토콜을 확장(extend)할 수 있습니다.
사용
- 프로토콜을 채택해서 특정 기능을 실행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있습니다.
- 프로토콜은 정의를 하고 제시를 할 뿐 스스로 기능을 구현하지는 않습니다. (조건만 정의)
- 하나의 타입으로 사용되기 때문에 아래와 같이 타입 사용이 허용되는 모든 곳에 프로토콜을 사용할 수 있습니다.
- 함수, 메서드, 이니셜라이저의 파라미터 혹은 리턴 타입
- 상수, 변수, 프로퍼티의 타입
- 배열, 딕셔너리의 원소타입
1-1, 프로토콜 정의
protocol SomeProtocol {
// protocol definition goes here
}
1-2, 프로토콜 채택
struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}
프로퍼티 요구사항 (Property Requirements)
- 프로토콜에서는 프로퍼티가 저장된 프로퍼티인지 계산된 프로퍼티인지 명시하지 않습니다.
- 하지만 프로퍼티의 이름과 타입 그리고 gettable, settable 한지는 명시합니다.
- 필수 프로퍼티는 항상 var로 선언해야 합니다.
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
}
타입 프로퍼티는 static 키워드를 적어 선언합니다.
protocol AnotherProtocol {
static var someTypeProperty: Int { get set }
}
하나의 프로퍼티를 갖는 프로토콜을 선언합니다.
protocol FullyNamed {
var fullName: String { get }
}
이 프로토콜을 채택하는 구조체를 선언합니다. fullName 프로퍼티는 저장된 프로퍼티로 사용될 수 있고,
struct Person: FullyNamed {
var fullName: String
}
let john = Person(fullName: "John Appleseed")
// john.fullName is "John Appleseed"
아래와 같이 연산 프로퍼티로 사용될 수 있습니다.
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
}
}
var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName is "USS Enterprise"
메서드 요구사항 (Method Requirements)
- 프로토콜에서는 필수 인스턴스 메서드와 타입 메소드를 명시할 수 있습니다.
- 하지만 메소드 파라미터의 기본 값은 프로토콜 안에서 사용할 수 없습니다.
protocol SomeProtocol {
static func someTypeMethod()
}
필수 메서드 지정시 함수명과 반환값을 지정할 수 있고, 구현에 사용하는 괄호는 적지 않아도 됩니다.
protocol RandomNumberGenerator {
func random() -> Double
}
다음 코드는 따르는 프로토콜의 필수 메소드 random()을 구현한 클래스입니다.
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()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And another one: \(generator.random())")
// Prints "And another one: 0.729023776863283"
이니셜라이저 요구사항 (Initializer Requirements)
- 프로토콜에서 필수로 구현해야 하는 이니셜라이저를 지정할 수 있습니다.
protocol SomeProtocol {
init(someParameter: Int)
}
클래스에서 프로토콜 필수 이니셜라이저의 구현 (Class Implementation of Protocol Initializer Requirements)
- 프로토콜에서 특정 이니셜라이저가 필요하다고 명시했기 때문에 구현에서 해당 이니셜라이저에 required 키워드를 붙여줘야 합니다.
클래스 타입에서 final로 선언된 것에는 required를 표시하지 않아도 됩니다. final로 선언되면 서브클래싱 되지 않기 때문입니다.
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
- 특정 프로토콜의 필수 이니셜라이저를 구현하고, 수퍼클래스의 이니셜라이저를 서브클래싱하는 경우 이니셜라이저 앞에 required 키워드와 override 키워드를 적어줍니다.
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
}
}
실패 가능한 초기자 요구사항 (Failable Initializer Requirements)
- 프로토콜에서 실패가능한 이니셜라이저를 선언할 수 있습니다.
위임 (Delegation)
- 위임은 클래스 혹은 구조체 인스턴스에 특정 행위에 대한 책임을 넘길 수 있게 해주는 디자인 패턴 중 하나입니다.
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)
}
프로토콜 익스텐션 (Protocol Extensions)
- 익스텐션을 이용해 프로토콜을 확장할 수 있습니다.
- 아래 코드는 random()을 따르는 RandomNumberGenerator에 randomBool()을 따르도록 추가한 예입니다.
extension RandomNumberGenerator {
func randomBool() -> Bool {
return random() > 0.5
}
}
아래 코드와 같이 generator에서 generator.random()과 generator.randomBool()를 둘 다 이용할 수 있음을 확인할 수 있습니다.
let generator = LinearCongruentialGenerator()
print("Here's a random number: \(generator.random())")
// Prints "Here's a random number: 0.3746499199817101"
print("And here's a random Boolean: \(generator.randomBool())")
// Prints "And here's a random Boolean: true"
익스텐션을 이용해 구현을 추가할 수는 있어도 다른 프로토콜로 확장/상속할 수는 없습니다. 만약 그렇게 하고자 한다면 익스텐션이 아닌 프로토콜 자체에 구현해야 합니다.
내용 참고 출처
https://jusung.gitbook.io/the-swift-language-guide/language-guide/21-protocols#protocol-extensions
'iOS > Swift 문법 복습' 카테고리의 다른 글
(인스턴스, 스태틱, 클래스) 메서드 (0) | 2022.06.30 |
---|---|
옵셔널 [Optional] (0) | 2022.06.28 |
프로퍼티 (Properties) (0) | 2022.06.27 |
Struct와 Class, Enum의 차이를 설명하시오. (0) | 2022.06.25 |
댓글