티스토리 뷰
@markdown
티스토리에 마크다운을 적용해보았으나 예쁘지 않다 😧 코드 색깔이 살지 않는다 😔
예쁘게 보려면 깃헙으로~!~!~!~!~!~!~!
[요기요기](https://github.com/eunjin3786/iOSStudy/blob/master/프로토콜.md ) 눌러보아요
# Protocol
## 1. 프로토콜 이란
프로토콜은 특정 역할을 하기 위한 메서드, 프로퍼티, 기타 요구사항 등의 청사진.
구조체, 클래스, 열거형 은 프로토콜을 채택해서 프로토콜의 요구사항을 실제로 구현한다
프로토콜의 요구사항을 모두 구현한 타입을 '해당 프로토콜을 conform(준수)한다' 라고 표현한다
```swift
protocol 프르토콜이름 {
프로토콜 정의
}
```
### 1.1 프로퍼티 요구사항
var키워드를 사용한 변수 프로퍼티로 정의한다
- 읽기 전용 프로퍼티는 { get }
- 읽기쓰기 모두 가능한 프로퍼티는 { get set }
```swift
protocol AProtocol {
var property : String { get }
var settableProperty : String { get set }
}
```
### 1.2 메소드 요구사항
```swift
protocol AProtocol {
func someFunction()
func someFunction(param1: String, param2 : String)
}
```
### 1.2.1 가변 메소드 요구사항
값 타입(구조체,열거형)의 인스턴스 메서드에서 자신 내부의 값을 변경하려고 하는 함수는 `mutating` 키워드를 써주기
```swift
protocol AProtocol {
mutating func someFunction()
}
```
### 1.3 타입 프로퍼티 or 타입 메소드 요구사항
> 타입 자체에 호출이 가능한 프로퍼티나 메소드 ( <-> 인스턴스 프로퍼티, 메소드 )
>
> 클래스의 타입 프로퍼티, 메소드는 static 키워드와 class키워드를 사용할 수 있는데
>
> static으로 정의하면 상속후 재정의(override)가 불가능하고 class로 정의하면 재정의가 가능하다.
`static` 키워드를 써준다
```swift
protocol AProtocol {
var property : String { get }
static var someTypeProperty: String { get }
static func someTypeMethod()
}
class ParentClass : AProtocol {
var property: String {
return "someText"
}
static var someTypeProperty: String {
return "someText"
}
static func someTypeMethod() {
}
}
// 이렇게 사용할 수 있다
ParentClass.someTypeProperty
ParentClass.someTypeMethod()
class ChildClass : ParentClass {
override var property: String {
return "AnotherText"
}
// static 붙은 프로퍼티랑 메소드는 오버라이드 할 수 없다-!
}
// 이렇게 사용할 수 있다
ChildClass.someTypeProperty
ChildClass.someTypeMethod()
```
## 2. 프로토콜 채택
타입 이름 뒤에 콜론(:)을 붙여준 후 채택할 프로토콜 이름을 쉼표( , ) 로 구분하여 명시해준다
```swift
struct SomeStruct : AProtocol, BProtocol {
}
class SomeClass : AProtocol, BProtocol {
}
enum SomeEnum : AProtocol, BProtocol {
}
```
- 예제 1
```swift
protocol AProtocol {
var property : String { get }
var settableProperty : String { get set }
}
class SomeClass : AProtocol {
var property: String {
return "someText"
}
var settableProperty: String{
get {
return "someText"
}
set {
// newValue를 가지고 어떤 것을 한다
}
}
}
```
- 예제 2
```swift
protocol Sendable {
var from : String { get }
var to : String { get }
func sendMessage(_ message:String)
}
class Message : Sendable {
var from: String {
return "eunjin"
}
var to: String {
return "heeyoun"
}
func sendMessage(_ message: String) {
print("\(from) -> \(to)")
}
}
class Mail : Sendable {
var from: String
var to: String
init(from:String,to:String) {
self.from = from
self.to = to
}
func sendMessage(_ message: String) {
print("\(from) -> \(to)")
}
}
```
- 예제 3 - `mutating`
```swift
protocol Resettable {
mutating func reset()
}
class Person : Resettable {
var name: String?
var age: Int?
func reset() {
self.name = nil
self.age = nil
}
}
struct Point: Resettable {
var x = 0
var y = 0
mutating func reset() {
self.x = 0
self.y = 0
}
}
enum Direction : Resettable {
case east, west, south, north, unknown
mutating func reset() {
self = Direction.unknown
}
}
```
## 3. 프로토콜의 이니셜라이저 요구
프로토콜은 특정한 이니셜라이저를 요구할 수 도 있다
```swift
protocol Named {
var name : String { get }
init(name: String)
}
struct Pet : Named {
var name : String
init(name: String) {
self.name = name
}
}
```
클래스에서 이니셜라이저를 요구한 프로토콜을 채택할때는 조금 다르다
`required` 식별자를 붙여줘야한다
```swift
class Person : Named {
var name : String
required init(name: String) {
self.name = name
}
}
```
만약 클래스가 상속받을 수 없는 final 클래스라면 `required` 식별자를 붙여줄 필요없다.
상속할 수 없는 클래스의 요청 이니셜라이저 구현은 무의미하기 때문
```swift
final class Person : Named {
var name : String
init(name: String) {
self.name = name
}
}
```
만약 상속받은 클래스가 있는 상황에서 이니셜라이저를 구현해줘야하는 프로토콜을 채택한다면 `required`와 `override` 식별자를 모두 명시하여야한다
```swift
class School {
var name: String
init(name: String) {
self.name = name
}
}
class MiddleSchool : School, Named {
required override init(name: String) {
super.init(name: name)
}
}
```
Shool 클래스에서 상속받은 init(name: ) 이니셜라이저를 재정의해야하고
동시에 Named 프로토콜의 이니셜라이저 요구도 충족시켜주어야하기 때문에
`required`와 `override` 식별자를 모두 표기해야한다
`override required`하든 `required override`하든 순서는 상관없다
## 4. 프로토콜의 상속
프로토콜은 하나 이상의 프로토콜을 상속받아 기존 프로토콜의 요구사항보다 더 많은 요구사항을 추가할 수 있다
```swift
protocol Readable {
func read()
}
protocol Writeable {
func write()
}
protocol ReadWriteSpeakable : Readable, Writeable {
func speak()
}
class SomeClass : ReadWriteSpeakable {
func read() {
}
func write() {
}
func speak () {
}
}
```
## 5. 클래스 전용 프로토콜의 정의
해당 프로토콜이 클래스타입에만 채택될 수 있도록 `class` 키워드를 추가해서 제한할 수 있다. 프로토콜의 상속 리스트의 맨 처음에 class 키워드가 위치해야한다.
```swift
protocol ClassOnlyProtocol : class, Readable, Writeable {
}
class SomeClass : ClassOnlyProtocol {
}
//오류발생-!
struct SomeStruct : ClassOnlyProtocol {
}
```
`
class ` 대신 `AnyObject` 키워드를 사용해도 된다.
```swift
protocol ClassOnlyProtocol : AnyObject, Readable, Writeable {
}
class SomeClass : ClassOnlyProtocol {
}
//오류발생-!
struct SomeStruct : ClassOnlyProtocol {
}
```
## 6. 프로토콜 조합 (Composition)
하나의 매개변수가 여러 프로토콜을 모두 준수하는 타입이어야한다면
& 를 사용하여 여러 프로토콜을 조합하여 요구할 수 있다
더불어 특정 클래스의 인스턴스 역할을 할 수 있는지 함께 확인 할 수 있다
```swift
var someVariable: AProtocol & BProtocol
var someVariable : AClass & AProtocol
// 에러발생
// 클래스 & 프로토콜 조합에서 클래스 타입은 한 타입만 조합할 수 있다
var someVariable : AClass & BClass & AProtocol
```
## 7. 프로토콜의 선택적 요구
프로토콜의 요구사항 중 일부를 선택적 요구사항으로 지정할 수 있다. 선택적 요구를 하면 해당 요구사항을 필수로 구현할 필요가 없다
- 제한조건
선택적 요구사항을 정의하고 싶은 프로토콜은 @objc 속성이 부여된 프로토콜이어야한다
------
@objc 속성
= 해당 프로토콜을 Objective-C코드에서 사용할 수 있도록 만드는 역할.
하지만 Objective-C코드 공유와 상관없이 @objc 속성이 부여되어야만 선택적 요구사항을 정의할 수 있다고 한다.
그리고 @objc 속성이 부여된 프로토콜은 클래스에서만 채택가능하다. 열거형이나 구조체에서 채택할 수 없다
------
선택적 요구사항은 optional 식별자를 요구사항의 정의 앞에 붙여주면 된다
```swift
@objc protocol Moveable {
func walk()
@objc optional func fly()
}
class Tiger : Moveable {
func walk() {
}
}
class Bird : Moveable {
func walk() {
}
func fly() {
}
}
```
## 8. 위임을 위한 프로토콜 - delegate 패턴
위임(delegation)은 클래스나 구조체가 자신의 책임이나 임무를 다른 타입의 인스턴스에게 위임하는 디자인 패턴.
사용자의 특정행동에 반응하기 위해, 비동기처리를 위해 주로 사용한다
ex) `UITableViewDelegate`
`UITableView` 타입의 인스턴스가 해야하는 일을 위임받아 처리하는 인스턴스는
`UITableViewDelegate` 프로토콜을 준수하면 된다. 그러면 `UITableView`의 인스턴스가 해야하는 일을 대신 처리해줄 수 있다.
- 예제 1
```swift
class Dice {
}
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate: AnyObject {
func gameDidStart(_ game: DiceGame)
func gameDidEnd(_ game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let dice = Dice()
weak var delegate: DiceGameDelegate?
func play() {
delegate?.gameDidStart(self)
// some code
delegate?.gameDidEnd(self)
}
}
class DiceGameTracker: DiceGameDelegate {
func gameDidStart(_ game: DiceGame) {
print("gameDidStart")
}
func gameDidEnd(_ game: DiceGame) {
print("gameDidEnd")
}
}
let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// 출력
// gameDidStart
// gameDidEnd
```
DiceGameDelegate 프로토콜은 DiceGame의 진행 상황을 추적하기 위해 채택될 수 있다.
DiceGameDelegate protocol은 class-only이고 strong reference cycle을 피하기 위해 weak로 선언한다.
- 예제 2 (옛날에 `delegate` 써보았던 예제)
```swift
protocol SettingDelegate{
func settingsDone(vm:SettingsViewModel)
}
class SettingsTableViewController: UITableViewController {
private var settingsViewModel = SettingsViewModel()
var delegate:SettingDelegate?
// settingsDone 액션을 delegate한테 위임한다
@IBAction func done(){
// weatherList VC 에서 넘겨준 delegate가 있으면
// weatherList VC에서 구현해놓은 delegate의 settingsDone 함수를
// 새로운 settingsViewModel을 넣어서 실행하도록 위임해줄게 -!
if let delegate = self.delegate{
delegate.settingsDone(vm: settingsViewModel)
}
self.dismiss(animated: true, completion: nil)
}
}
```
```swift
class WeatherListTableViewController: UITableViewController {
private var weatherListViewModel = WeatherListViewModel()
// SettingsTVC로 넘어갈 때
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nav = segue.destination as? UINavigationController else { fatalError("Navigation Controller not fiound") }
guard let settingsTVC = nav.viewControllers.first as? SettingsTableViewController else{ fatalError("SettingsTableViewController not found") }
settingsTVC.delegate = self
}
}
extension WeatherListTableViewController: SettingDelegate {
func settingsDone(vm: SettingsViewModel) {
self.weatherListViewModel.updateUnit(to: vm.selectedUnit)
tableView.reloadData()
}
}
```
## 9. extension
`extension` 을 통해 필요한 기능을 구현해두면, 그 프로토콜을 채택하는 인스턴스들에서 공통으로 그 기능을 사용할 수 있다
```swift
Protocol AProtocol {
}
extension AProtoco {
func someFunction () {
}
}
```
`extension`에 `where` 절을 사용하면 `where`절 뒤에 적혀있는 특정 프로토콜을 준수하는 타입에만
이 `extension`이 적용될 수 있도록 제약을 줄 수 있다.
```swift
// AProtocol을 채택하는 타입 중 BProtocol도 채택하는 타입에만 이 익스텐션이 적용될 수 있다
extension AProtocol where Self : BProtocol {
}
// AProtocol을 채택하는 타입 중 BProtocol,CProtocol 도 채택하는 타입에만 이 익스텐션이 적용될 수 있다
extension AProtocol where Self : BProtocol, Self : CProtocol {
}
```
where 써주는 것이 귀찮아서 콜론(:) 으로 해보았으나
```
extension AProtocol: BProtocol {
}
```
📌 ' Extension of protocol 'AProtocol' cannot have an inheritance clause '
이라고 컴파일 에러가 뜬다 🤔
## 10. Protocol Type
프로토콜을 타입으로 사용할 수 있다
그리고 `array` 나 `dictionary` 같은 `collection`에 저장될 유형으로 사용할 수 있다.
```swift
protocol TextRepresentable {
}
class SomeClass : TextRepresentable {
}
struct SomeStruct : TextRepresentable {
}
let someClass = SomeClass()
let someStruct = SomeStruct()
let array: [TextRepresentable] = [someClass, someStruct]
```
### Reference
- 야곰의 스위프트 프로그래밍
- https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
'🍏 > iOS' 카테고리의 다른 글
[Popup] 팝업 띄우기 (0) | 2019.05.23 |
---|---|
[AutoLayout] Hugging priority와 Compression Resistance priority 비교 (2) | 2019.04.25 |
[Enum] enum 꿀팁 - CaseIterable, Extension (0) | 2018.12.31 |
[CodingKey] CodingKey로 JSON DECODING을 예쁘게 (0) | 2018.12.31 |
[StatusBar] StatusBar 색깔을 White로 바꾸자 (0) | 2018.12.29 |
- Total
- Today
- Yesterday
- Sketch 누끼
- Django FCM
- Flutter Clipboard
- DRF APIException
- ipad multitasking
- 장고 URL querystring
- flutter deep link
- SerializerMethodField
- ribs
- Flutter 로딩
- 플러터 얼럿
- drf custom error
- github actions
- flutter dynamic link
- PencilKit
- 플러터 싱글톤
- flutter build mode
- Flutter Spacer
- cocoapod
- 구글 Geocoding API
- Python Type Hint
- Flutter getter setter
- 장고 Custom Management Command
- Dart Factory
- Watch App for iOS App vs Watch App
- METAL
- Django Heroku Scheduler
- Django Firebase Cloud Messaging
- flutter 앱 출시
- Flutter Text Gradient
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |