티스토리 뷰

Swift/야곰의 스위프트

20장 프로토콜

할루루 2018. 3. 1. 12:51

[프로토콜이란]


- 프로토콜 Protocol : 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진

- 구조체, 클래스, 열거형은 프로토콜을 채택하여 특정 기능을 실행하기 위한 프로토콜의 요구사항을 실제로 구현할 수 있음

 : 해당 프로토콜을 준수Conform함

- 프로토콜은 정의를 하고 제시를 할 뿐 스스로 기능을 구현하지 않음

- Java의 인터페이스와 비슷한 개념?



[프로토콜 정의]


- protocol 키워드를 사용

1
2
3
protocol 프로토콜 이름{
    프로토콜 정의
}
cs


- 프로토콜을 채택하려면 타입 이름 뒤에 콜론을 붙여준 후 채택할 프로토콜 이름을 쉼표로 구분하여 명시해줌

- 클래스가 다른 클래스를 상속받는다면 상속받을 클래스 이름 다음부터 채택할 프로토콜을 나열해줌



[프로토콜 요구사항 - 프로퍼티 요구]


- 프로토콜은 자신을 채택한 타입이 어떤 프로퍼티를 구현해야 하는지 요구할 수 있음

- 프로퍼티를 읽기 전용으로 할지 읽고 쓰기가 가능하게 할지는 프로토콜에서 정해야 함

- 항상 var 키워드를 사용한 변수 프로퍼티로 정의

- 읽고 쓰기가 모두 가능한 프로퍼티는 프로퍼티 정의 뒤에 { get set }

- 읽기 전용 프로퍼티는 프로퍼티 정의 뒤에 { get }

- 쓰기 전용 프로퍼티는 원래 없음

- 타입 프로퍼티를 요구하려면 static 키워드를 사용하여 정의

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protocol Sendable{
    var from: String { get }
    var to: String { get }
}
 
class Message: Sendable{
    var sender: String
    var from: String{
        return self.sender
    }
    var to: String
    init(sender: String, receiver: String) {
        self.sender = sender
        self.to = receiver
    }
}
class Mail: Sendable{
    var from: String
    var to: String
    init(sender: String, receiver: String) {
        self.from = sender
        self.to = receiver
    }
}
 
cs


위의 코드에서 from 프로퍼티는 프로토콜에서 읽기 전용으로 정의되었지만 실제로 프로토콜을 채택한 클래스에서 구현할 때는 읽고 쓰기가 가능한 프로퍼티로 구현해도 문제가 없음



[프로토콜 요구사항 - 메서드 요구]


- 프로토콜은 특정 인스턴스 메서드나 타입 메서드를 요구할 수 있음

- 메서드의 실제 구현부는 제외하고 메서드의 이름, 매개변수, 반환 타입 등만 작성, 가변 매개변수 허용

- 매개변수 기본값을 지정할 수 없음

- 타입 메서드를 요구하려면 static 키워드를 사용하여 정의, 실제 구현할 때는 static, class 어느 쪽을 사용해도 무방함

1
2
3
4
5
6
7
8
9
10
protocol Receivable{
    func  received(data: Any, from: Sendable)
}
protocol  Sendable {
    var from: Sendable { get }
    var to: Receivable? { get }
    func send(data: Any)
    static func isSendableInstance(_ instance: Any) -> Bool
}
 
cs


프로토콜 타입의 인스턴스는 해당 프로토콜을 준수하는 타입의 인스턴스라고 생각할 수 있음



[프로토콜 요구사항 - 가변 메서드 요구]


- 프로토콜이 어떤 타입이든 간에 인스턴스 내부의 값을 변경해야 하는 메서드를 요구하려면 mutating 키워드를 명시해야 함

- 참조 타입(클래스)에서 해당 메서드를 구현할 때는 mutating 키워드를 사용하지 않아도 됨

- 값 타입(구조체, 열거형)에서 해당 메서드를 구현할 때는 mutating 키워드를 명시해야 함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protocol Resetable{
    mutating func reset()
}
 
class Person: Resetable{
    var name: String?
    var age: Int?
    func reset() {
        self.name = nil
        self.age = nil
    }
}
struct Point: Resetable{
    var x: Int = 0
    var y: Int = 0
    mutating func reset() {
        self.x = 0
        self.y = 0
    }
}
enum Direction: Resetable{
    case east, west, north, south, unknown
    mutating func reset() {
        self = Direction.unknown
    }
}
cs




[프로토콜 요구사항 - 이니셜라이저 요구]


- 클래스의 경우 생성자 요구에 부합하는 생성자를 구현할 때는 required 키워드를 붙인 요구 생성자로 구현해야 함

 : 해당 클래스를 다른 클래스가 상속받는 경우 해당 생성자를 구현해야 함

- 클래스가 상속받을 수 없는 final 클래스라면 상속이 불가능하므로 required를 붙여줄 필요가 없음

- 특정 클래스에 프로토콜이 요구하는 생성자가 이미 구현되어 있는 상황에서 그 클래스를 상속받은 클래스가 있다면 requiredoverride를 모두 명시함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
protocol Named{
    var name: String { get }
    init(name: String)
}
 
struct Pet: Named{
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Person: Named{
    var name: String
    required init(name: String) {
        self.name = name
    }
}
 
class School{
    var name: String
    init(name: String) {
        self.name = name
    }
}
class MiddleSchool: School, Named{
    override required init(name: String) {
        super.init(name: name)
    }
}
cs




[프로토콜의 상속과 클래스 전용 프로토콜]


- 프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있음

- 프로토콜의 상속 리스트에 class 키워드를 추가하여 프로토콜이 클래스 타입에만 채택될 수 있게 할 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protocol Readable{
    func read()
}
protocol Writeable{
    func write()
}
protocol ReadSpeakable: Readable{
    func speak()
}
protocol ReadWriteSpeakable: Readable, Writeable{
    func speak()
}
class SomeClass: ReadWriteSpeakable{
    func speak() {
        print("speak")
    }
    
    func read() {
        print("read")
    }
    
    func write() {
        print("write")
    }
}
 
cs




[프로토콜 조합과 프로토콜 준수 확인]


- 하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야 한다면 하나의 매개변수에 여러 프로토콜을 한번에 조합하여 요구할 수 있음

- AProtocol & BProtocol과 같이 표현 : 프로토콜 이름 사이에 &를 써줌


- 구조체나 열거형 타입은 조합할 수 없음

- 클래스 타입은 한 타입만 조합할 수 있음

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
protocol Named{
    var name: String { get }
}
protocol Aged{
    var age: Int { get }
}
struct Person: Named, Aged{
    var name: String
    var age: Int
}
class Car: Named{
    var name: String
    init(name: String) {
        self.name = name
    }
}
class Truck: Car, Aged{
    var age: Int
    init(name: String, age: Int) {
        self.age = age
        super.init(name: name)
    }
}
 
 
func function(to celebrator: Named & Aged){
    print("\(celebrator.name), \(celebrator.age)")
}
let me = Person(name: "Me", age: 24)
function(to: me)
 
let variable: Car & Aged = Truck(name: "Truck", age: 13)
 
cs


위의 코드에서 상수 mePerson 타입의 인스턴스가 할당되었고 이는 NamedAged 프로토콜을 모두 준수하므로 함수의 매개변수가 될 수 있음

상수 variableCar를 상속받고 Aged 프로토콜을 준수하는 Truck의 인스턴스가 할당됨


- is 연산자를 사용하여 대상이 프로토콜을 준수하는지 확인 가능

- as? 연산자를 사용하여 다른 프로토콜로 다운캐스팅을 시도할 수 있음

- as! 연산자를 사용하여 다른 프로토콜로 강제 다운캐스팅을 할 수 있음



[프로토콜의 선택적 요구]


- 프로토콜의 요구사항 중 일부를 선택적 요구사항을 지정할 수 있음

- @objc 속성이 부여된 프로토콜이어야 함

 : @objc : 해당 프로토콜을 Objective-C 코드에서 사용할 수 있도록 만듦

 : Objective-C 클래스를 상속받은 클래스에서만 채택할 수 있음 -> 열거형이나 구조체에서는 @objc 속성이 부여된 프로토콜은 채택할 수 없음

- optional 식별자를 요구사항의 정의 앞에 명시함

 : 해당 요구사항의 타입은 옵셔널이 됨

 : 메서드 자체의 타입이 옵셔널이 된 것임

- 선택적 요구사항은 그 프로토콜을 준수하는 타입에 구현되어 있지 않을 수 있으므로 옵셔널 체이닝을 통해 호출함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import Foundation
 
@objc protocol Moveable{
    func walk()
    @objc optional func fly()
}
class Tiger: NSObject, Moveable{
    func walk() {
        print("Tiger Walks")
    }
}
class Bird: NSObject, Moveable{
    func walk() {
        print("Bird Walks")
    }
    func fly() {
        print("Bird Flys")
    }
}
 
let tiger = Tiger()
let bird = Bird()
 
tiger.walk()
bird.walk()
bird.fly()
cs




[프로토콜 변수와 상수]


- 프로토콜 이름을 타입으로 갖는 변수나 상수에는 그 프로토콜을 준수하는 타입의 어떠한 인스턴스라도 할당할 수 있음

- 프로토콜 이름만으로 자기 스스로 인스턴스를 생성하고 초기화할 수는 없음



[위임을 위한 프로토콜]


- 위임Delegation : 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴

- 책무를 위임하기 위해 정의한 프로토콜을 준수하는 타입은 자신에게 위임될 일정 책무를 할 수 있다는 것을 보장함

- 위임은 사용자의 특정 행동에 반응하기 위해 사용되기도 하며, 비동기 처리에도 많이 사용함


- 위임 패턴은 애플의 프레임워크에서 사용하는 주요한 패턴 중 하나

- [~~]Delegate와 같은 이름의 프로토콜은 위임 패턴을 위해 정의된 프로토콜임

'Swift > 야곰의 스위프트' 카테고리의 다른 글

22장 제네릭  (0) 2018.04.27
21장 익스텐션  (0) 2018.03.07
19장 타입캐스팅  (0) 2018.03.01
18장 상속  (0) 2018.02.28
17장 서브스크립트  (0) 2018.02.28
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함