![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate]](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
타입으로서의 프로토콜
- 프로토콜은 다른 타입이 허용되는 여러 곳에서 다음과 같은 프로토콜을 사용할 수 있다.
- 함수, 메서드 또는 이니셜라이저에서의 매개변수 타입 또는 리턴 타입
- 상수, 변수 또는 프로퍼티로서의 타입
- 배열 또는 딕셔너리 또는 다른 컨테이너의 항목으로서의 타입
타입으로서의 프로토콜
프로토콜은 요구만 하고 스스로 기능을 구현하지 않는다. 하지만 프로토콜은 코드에서 완전한 하나의 타입으로 사용되기 때문에 여러 위치에서 프로토콜을 타입으로 사용할 수 있다.
프로토콜은 이름을 정할 때 대문자 카멜 케이스를 사용하기 때문에 SomeProtocol
, AnotherProtocol
처럼 첫 글자를 반드시 대문자로 표현해야 한다.
protocol RandemNumberGenrator { func random() -> Double }
RandemNumberGenrator
프로토콜에서 메서드를 요구했고, 이 프로토콜을 채택하는 타입은 반드시 random()
메서드를 구현해야 한다.
이제 타입으로서의 프로토콜을 사용해보자!
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 } } class Dice { let sides: Int //클래스에서 RandomNumberGenrator을 채택하지 않고 //genrator 상수 저장 프로퍼티에 타입으로 채택 해줬다. let generator: RandomNumberGenerator //Dice클래스의 저장 프로퍼티에 기본값이 없기때문에 init으로 초기화 //generator은 RandomNumberGenrator타입 이기 때문에 random()메서드 구현의무 없음(접근은 가능) init(sides: Int, generator: RandomNumberGenerator) { self.sides = sides self.generator = generator } func roll() -> Int { return Int(generator.random() * Double(sides)) + 1 } } var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator()) for _ in 1...5 { print("Random dice roll is \(d6.roll())") }
위 코드를 보면 Dice
자식클래스에 RandemNumberGenrator
프로토콜을 채택하지 않고 상수 저장 프로퍼티에 RandemNumberGenrator
타입으로 준 것을 확인할 수 있다. 그래서 generator
는 RandemNumberGenrator
타입이 된다.
또, 클래스의 저장프로퍼티에 기본값이 없기 때문에 init
으로 초기화를 해주고, generator
은 RandemNumberGenrator
타입이기 때문에 random()
이라는 메서드 요구를 구현할 필요가 없다. random()
메서드에 접근은 가능하다.
Delegate 패턴
"Delegate는 클래스나 구조체가 책임을 일부 다른 타입의 인스턴스로 전달 또는 위임 할 수 있게 하는 Design Pattern이다."
Delegate
패턴은 iOS 개발에서 자주 사용되는 디자인 패턴이다.
이것을 사용하지 않고 iOS 앱을 개발하는 것은 거의 불가능에 가깝다고 한다.
Delegate
란 단어는 '위임하다'라는 사전적 의미 가지고 있듯이, '위임자'라고 생각하면 된다. Delegate
패턴은 delegate
즉, 위임자를 갖고 있는 객체가 다른 객체에게 자신의 일을 위임하는 형태의 디자인 패턴이다.
위에 설명이 이해가 안 가는데.. 아래에서 쉽게 풀어보자면!
일단 이 Delegate
패턴을 실제로 작동하게 하는 기술이 바로 Protocol
이다.
대표적인 예시로 UITableView Class
는 UITableViewDelegate
프로토콜과 UITableViewDataSource
라는 프로토콜을 통해서 Table
을 관리, 상호작용하고 셀을 보여준다.
또, UITextView
는 UITextViewDelegate
프로토콜을 사용하여 TextView
내부의 상태변화(작성 시작, 중지 등..)를 보고받는다. TextView
는 사용자의 입력에 따라서 응답하고 이것에 따라 Delegate
기능을 호출하게 된다.
예를 들어 아래 예시는 TextView
가 포함되어 있고 해당 TextView
는 delegation
을 사용한다.
class SomeViewController: UIViewController, UITextViewDelegate { var textView: UITextView func viewDidLoad() { textView.delegate = self // TextViewDelegate는 SomeViewController의 인스턴스 } }
UIViewController
를 상속받는 SomeViewController
를 생성했고, 동시에 UITextViewDelegate
라는 프로토콜도 채택했다. class
내부에는 UITextView
의 인스턴스인 textView
를 선언했고 viewDeleLoad
매소드에 textView.delegate
를 self
로 지정했다.
이렇게 UITextViewDelegate
를 보면, textView
에서 발생하는 모든 이벤트에 응답하기 위해 다양한 함수를 구현할 수 있는 것이다.
textViewDidBeginEditing(_:)
textViewDidEndEditing(_:)
textView(_:shouldChangeTextIn: replacementText:)
textViewDidChange(_:)
textViewDidChange
기능을 통해 textView
에 있는 문자 수를 업데이트하는 등의 기능을 사용할 수 있는 것이다.
또 다른 예를 보자면,
만약 UITextField
를 사용하여 iOS
앱을 개발할 때 UITextField
의 모든 메서드나 프로퍼티를 처음부터 만들어 나간다.
텍스트 필드의 모양, 모서리, cornerRadius
등..
그리고 WeatherViewController
에 TextFieldDidBeginEditing()
메서드를 생성한다고 가정해보자. WeatherViewController
가 편집을 감지할 때 이를 알리기 위해선 텍스트 필드가 필요한데, 이것을 작동하려면 UITextField
에 대한 클래스 정의를 UITextField
에 만들고 거기에서 textFieldDidBeginEditing
메서드를 호출하면 된다.
이렇게!
![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴](https://blog.kakaocdn.net/dn/drq4kO/btrrQ7s1Hfc/kEsVe4b0a8KaebRhY7cwYk/img.png)
하지만 또 다른 AnotherClass
가 있고 그곳에 textFieldDidBeginEditing
메서드를 사용하려고 할 때 또다시 메서드를 클래스 안에 생성해야 할까? 이럴 때 필요한 것이 바로 Delegate Design Pattern!
솔루션은 이러하다.
![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴](https://blog.kakaocdn.net/dn/bvx8CP/btrrW3oUwhB/KPLfnzHFkapynWpCcbRQMk/img.png)
UITextFieldDelegate
프로토콜을 생성하고textFieldDidBeginEditing
메서드 생성.UITextField
에delegate
변수를 생성하고 반드시UITextFieldDelegate
를 데이터 타입으로 채택.WeatherViewController
에UITextField
를 새롭게 초기화하여 생성하고.TextField.delegate
를self
로 지정해준다.- 마지막으로
textFieldDidBeginEditing
메서드를 호출.
이렇게 하면 또 다른 Class
가 있어도 UITextField
를 수정하지 않고 그대로 사용 가능하다는 장점이 있다.
![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴](https://blog.kakaocdn.net/dn/c0B3My/btrrXL9kOzm/G2NB0OnF7948fKvh6O4hPk/img.png)
이처럼 Delegate
를 통해서 업무를 위임하는 것이 아니라 기능을 확장하기 위해서 사용하는 것이 바로 Delegate Design Pattern 이다.
![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴](https://blog.kakaocdn.net/dn/borTtO/btrrW1YSKp9/nBfOuKUFTQ90m7WKeYqDCk/img.png)
클래스 만들어보기
UITextField
를 사용할 때, 프로토콜과 Delegate
에 대해 더 깊게 알아보도록 하자! (엄청 복잡하기 때문에.. 정신줄 단단히!!!)
Udemy에서 나온 Delegate 패턴
에 대해 인용해서 가져와보자면
![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 - 클래스 만들어보기 Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 - 클래스 만들어보기](https://blog.kakaocdn.net/dn/KnIjh/btrrUMPcaV0/3cgfJxWRC3eK6L8qPDHtKK/img.png)
1. EmergenctCallHandler
라는 콜센터가 있고, 호출기(Delegate
)가 있다.
2. 이 Delegate
를 소지할 수 있는 사람은 CPR을 할 줄 알아야 하고, 이것은 AdvancedLifeSupport라는 인증서(Protocol
)이 있어야 한다. 즉, 이 프로토콜을 소지하고 있는 사람은 Delegate
를 가질 수 있는 조건이 있으며 그게 어떠한 사람이던(Class
) 상관없다.
코드로 알아보자면 아래와 같다.
//MARK: 1 protocol AdvancedLifeSupport { func performCPR() //COR을 수행할줄 알아야함. } //MARK: 2 class EmergencyCallHandler { //delegate로 AdvancedLifeSupport가 채택되었기 때문에 반드시 CPR을 할 줄 알아야 함. //또, 반듯이 이 delegate는 AdvancedLifeSupport 데이터 타입을 채택해야 함. var delegate: AdvancedLifeSupport? func assessSituation(Director: String) { print("무슨일이 일어나고있는지 \(Director)에게 전달해주세요.") } //이 메서드는 대리인이 누구인지 상관없다. //delegate를 가지고있다면 CPR을 진행하라 에만 관심이 있음. func medicalEmergency() { delegate?.performCPR() } } //MARK: 3 //대리인 지정 struct Paramedic: AdvancedLifeSupport { //EmergencyCallHandler가 초기화 될때 init(handler: EmergencyCallHandler) { handler.delegate = self } //어떻게 CPR을 할지 메서드 작성 func performCPR() { print("위생병이 가슴압박 30회, 인공호흡 2회 시행, 가슴압박 및 인공호흡 반복한다.") } } //대리인 class Doctor: AdvancedLifeSupport { init(handler: EmergencyCallHandler) { handler.delegate = self } func performCPR() { print("의사가 가슴압박 30회, 인공호흡 2회 시행, 가슴압박 및 인공호흡 반복한다.") } func useStethoscope() { print("청진기를 사용하여 심장소리를 듣는다") } } //대리인 //Doctor 클래스를 참조한다. class Surgeon: Doctor { //슈퍼클래스인 Doctor가 하는 CPR을 동일하게 수행가능 override func performCPR() { super.performCPR() print("외과의사가 의사가하는 일을 옆에서 서포트 한다.") } func useElectricDrill() { print("외과의사가 피를 석션 한다.") } } //MARK: 4 let Seogun = EmergencyCallHandler() let Mijin = Surgeon(handler: Seogun) Seogun.assessSituation(Director: "서근") Seogun.medicalEmergency() //무슨일이 일어나고있는지 서근에게 전달해주세요. //의사가 가슴압박 30회, 인공호흡 2회 시행, 가슴압박 및 인공호흡 반복한다. //외과의사가 의사가하는 일을 옆에서 서포트 한다.
![Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 - 클래스 만들어보기 Swift : 기초문법 [프로토콜#3 - 타입으로서의 프로토콜, Delegate] - Delegate 패턴 - 클래스 만들어보기](https://blog.kakaocdn.net/dn/mKkAo/btrrXLIgCuK/jx81i4jvfgIDyVv8JkZ4b0/img.png)
Delegate
를 채택해서 어떤 일을 할지 결정하고, 위임자를 설정해서 누가 누구의 일을 대신 맡을지 정해주고, 특정 상황에서 무슨 일을 대신해주면 되는지 작성해주면 Delegate Design Pattern
을 사용할 수 있다.
Delegation을 사용하는 이유
1. Delegate
는 protocol
을 준수하는 것만으로도 구현이 가능하므로 매우 유연하다.
2. 완전한 Class
또는 Struct
를 상속할 필요가 없기 때문에 Delegate
를 더 가볍게 사용할 수 있다.
3. Class
간 요구 사항을 전달해주는 protocol
만 있으면 연결이 쉽다.
4. Delegate
, Delegate Design Pattern
은 한 Class
와 또 다른 Class
의 상호작용을 간단히 할 수 있도록 돕는다.
읽어주셔서 감사합니다 🤟
'SWIFT > Grammar' 카테고리의 다른 글
Swift : 기초문법 [프로토콜#5 - 준수, 옵셔널 프로토콜 요구사항] (0) | 2022.01.31 |
---|---|
Swift : 기초문법 [프로토콜#4 - 익스텐션, 컬렉션 타입, 상속] (0) | 2022.01.30 |
Swift : 기초문법 [프로토콜#2 - 메서드 요구사항] (0) | 2022.01.27 |
Swift : 기초문법 [프로토콜#1 - 프로퍼티 요구사항] (1) | 2022.01.25 |
Swift : 기초문법 [타입 캐스팅(Type Casting)] (1) | 2022.01.24 |