swift study 16. 프로토콜 (Protocols)

jess·2022년 7월 25일
0

Swift

목록 보기
16/19
post-thumbnail

🍃 출처 : 앨런 swift 문법 마스터 스쿨 수업을 듣고 제가 이해한대로 정리해서 올리는 포스팅입니다.

📓 프로토콜 (Protocols)

1) 프로토콜의 개념

- 상속의 방향과 프로토콜의 필요성
✏️ 클래스와 상속의 단점
1. 하나의 클래스만 상속 가능 (다중 상속 불가능)
2. 기본적인 상위클래스의 (저장속성의) 메모리구조를 따라갈 수 밖에 없음
(필요하지 않은 속성/메서드도 상속됨)
3. 클래스(레퍼런스 타입)에서만 가능

  • 영어로 규약이나 협약 -> 자격증 / 리모콘 같은 개념

2) 프로토콜의 문법

class Student: Person, AProtocol, BProtocol {
    // A프로토콜, B프토토콜이 원하는 내용 구현해야 함
}
// (1) 정의
protocol MyProtocol {
    // "요구사항만"을 정의 (구체적인 구현 없음, 프로토콜을 채택한 곳에서 하게 됨)
    func doing() -> Int
    }
}
// (2) 채택
class Class: MyProtocol {
}
// (3)구현
func doing() -> Int {
    return 7
}
// <상속이 있는 경우의 문법>
class Person: Student, MyProtocol { ... }
1) 상속하려는 클래스 먼저 선언
2) 그 다음 채택하려는 프로토콜 선언

✏️ 요구사항을 정의하는 방법

1️⃣ 속성의 요구사항을 정의

protocol MyProtocol {
    var id: String { get }
    var name: String { get set }
    static var type: String { get set }
            // 속성 의미의 var, let은 안됨 (get 받아와야하니까)
}
 ✨ 인스턴스 속성 요구사항
  - 최소한의 요구사항을 지정 (헤드부분-인풋,아웃풋의 형태만)
  - 저장속성 / 계산속성으로 모두 구현 가능
    (프로토콜 요구사항만으로는 저장/계산 속성의 구별 불가)
  ✨ 타입(static) 속성 요구사항
   - 최소한의 요구사항을 지정
   - 저장 타입 속성 / 계산 타입 속성으로 모두 구현 가능 
      (저장 속성 재정의 불가 원칙)
   - 클래스에서 채택 시 계산 타입 속성에서 
     static / class 키워드로 모두 구현 가능
     (타입 속성의 의미일 뿐 / class 키워드는 재정의 가능) 

2️⃣ 메서드의 요구사항을 정의

protocol MyProtocol {
    func random() -> Int
    mutating func toggle()
    static func reset()
}
 ✨ 메서드 요구사항
  - 메서드의 헤드부분 (인풋/아웃풋)을 정의
  - mutatigng 키워드 : (구조체로 제한하는 것은 아님) 
                      구조체에서 저장 속성 변경하는 경우, 
                      구조체도 채택 가능하도록
                      허락하는 키워드
  - 타입 메서드 : 클래스에서 채택 시 static / class 키워드로 모두 구현 가능
               (타입 메서드의 의미, class 키워드는 재정의 가능)
protocol MyProtocol {
    init()
    subscript(idx: Int) -> Int { get set }
                              // 서브스크립트에서 get필수 (set선택)
}
 ✨ 생성자 요구사항
 - 생성자를 요구사항으로 지정 가능
 1) 클래스에서 생성자 채택 시, (하위클래스를 고려) required(필수적)를 붙여야 함
 2) 클래스가 final로 선언되려면 required 생략 가능
 3) 클래스에서는 반드시 지정생성자로 구현할 필요 없음 (편의생성자로도 구현 가능)
 init?() 실패가능 생성자 선언 ➡️ init() / init!() 로 구현 (O)
 init() 실패불가능 생성자 선언 ➡️ init?() 로 구현 (X)
 ✨ 서브스크립트 요구사항
 -  최소한의 요구 사항을 지정
 { get } ➡ 읽기(get) / 읽기쓰기(get/set) 
 { get set } ➡ 읽기쓰기(get/set)

✏️ 상속과 프로토콜 채택의 문법

extension SomeType:   {
    // 프로토콜이 원하는 내용 구현
}
  • 관습적으로 프로토콜의 채택은 확장(extension)에서 구현하는 것을 권장

3) 타입으로써의 프로토콜과 기타 문법

15-4

✏️ 프로토콜은 타입이다.

 - 스위프트는 프로토콜을 "일급객체" 로 취급

1) "프로토콜"을 변수에 할당할 수 있음
2) 함수를 호출할 때, "프로토콜"을 파라미터로 전달할 수 있음
3) 함수에서 "프로토콜"을 반환할 수 있음

// 프로토콜의 타입 취급의 장점 - 1 ⭐️

let electronic: [Remote] = [tv, sbox]      // 프로토콜의 형식으로 담겨있음


for item in electronic {   // 켜기, 끄기 기능만 사용하니 
                             타입캐스팅을 쓸 필요도 없음 
                            (다만, 프로토콜에 있는 멤버만 사용가능)
    item.turnOn()
}




// 프로토콜의 타입 취급의 장점 - 2 ⭐️

func turnOnSomeElectronics(item: Remote) {
    item.turnOn()
}

turnOnSomeElectronics(item: tv)
turnOnSomeElectronics(item: sbox)

✏️ is연산자 / as연산자 for 프로토콜

// 1) is연산자 ==============================

// 특정타입이 프로토콜을 채택하고 있는지 확인
tv is Remote
sbox is Remote

// 프로토콜 타입으로 저장된 인스턴스가 더 구체적인 타입인지 확인 가능
electronic[0] is TV
electronic[1] is SetTopBox


// 2) as연산자 ==============================

// 업캐스팅(as)
let newBox = sbox as Remote
newBox.turnOn()
newBox.turnOff()

// 다운캐스팅(as?/as!)
let sbox2: SetTopBox? = electronic[1] as? SetTopBox
sbox2?.doNetflix()

//(electronic[1] as? SetTopBox)?.doNetflix()

✏️ 프로토콜의 상속, 다중상속

  • 실제 프로토콜의 상속까지 구현할 일은 잘 없지만,
    애플이 만들어놓은 체계에서는 많이 쓰이고 있음
    ㅡ A

✏️ 클래스 전용 프로토콜

protocol SomeType: AnyObject {
    // 최소한의 요구사항
}
  • AnyObject는 프로토콜이다. 그렇기 때문에 범용적 타입으로 사용할 수 있었던 것이고,
    다운 캐스팅(as?/as!)해서 구체적인 실제타입으로 사용할 수 있었던 것

✏️ 프로토콜의 합성

let some: AProtocol & BProtocol = 인스턴스
  • 프로토콜을 &로 연결해서,
    프로토콜 두 개를 병합해서 타입으로 사용하는 것 가능
    ex) Aprotocol & BProtocol

4) 프로토콜의 선택적(optional) 요구사항의 구현

                 (구현을 해도 되고 안해도 되는)

✏️ @어트리뷰트 키워드 짚고 가기

  • 추가적인 정보를 제공하는 키워드
    // (1) 선언에 추가정보 제공해주는 종류
    @available(iOS 10.0)
    class MyClass {
       ...
    }
    // (2) 타입에 추가 정보 제공해주는 종류 
    func doSomething(completion: @escaping() -> ()) {
       ...
    }
    // - 컴파일러에게 추가적인 정보를 알려주는 역할 (2가지 종류)

✏️ 선택적 요구사항의 구현

요구사항을 강제가 아닌 선택적으로 만들기

@objec protocol MyProtocol {
    var name: String { get }
    @objc optional var isOn: Bool { get set }
    ...
    @objc optional func doSomething()
}
 ✨ 선택적(optional) 멤버로 선언
  - 프로토콜에서 요구사항 구현 시, 선택적인 멤버로 구현가능하도록 변형 가능
    (다만, 본 기능은 오브젝티브C에서 지원하는 기능)
  - @objc키워드를 프로토콜의 선언앞에 붙여서, 추가적인 정보 제공
     (오브젝티브C에서 읽을 수 있는 코드라는 의미) 
  - @objc optional 을 멤버 앞에 선언 ➡ 
    해당 멤버는 선택적 요구사항으로 바뀜 
     (클래스 전용 프로토콜이기 때문에, 구조체 / 열거형에서는 채택하지 못함) 

5) 프로토콜의 확장과 프로토콜지향 프로그래밍

15-7

/**========================================
 - (귀찮은 방식으로) 프로토콜을 채택한 모든 타입에서, 
 실제 구현을 계속적으로 반복해야하는 불편함을 덜기 위해
 - "프로토콜 확장"을 제공해서 메서드의 
 디폴트 구현을 제공함 (코드의 중복을 피할 수 있음)
==========================================**/


extension Remote {                         
                // (요구사항의 메서드 우선순위 적용 - 프로토콜 메서드 테이블 만듦)
    func turnOn() { print("리모콘 켜기") }    
           // 1. (채택)구현시 해당 메서드 2. 기본 메서드
    func turnOff() { print("리모콘 끄기") }   
          // 1. (채택)구현시 해당 메서드 2. 기본 메서드
    
    func doAnotherAction() {               
         // (요구사항 메서드 X - 테이블 만들지 않음)
        print("리모콘 또 다른 동작")            
           // 타입에 따른 선택 (Direct Dispatch)
    }
}

✨ 클래스와 상속의 단점
1. 하나의 클래스만 상속가능 (다중상속 불가능)
2. 기본적인 상위클래스의 메모리구조를 따라갈 수 밖에 없음
(필요하지 않은 속성/ 메서드도 상속됨)
3. 클래스(레퍼런스타입) 에서만 가능

✨ 프로토콜 지향 프로그래밍
1. 여러개의 프로토콜 채택 가능 (다중상속과 유사)
2. 메모리 구조에 대한 특정 요구사항 없음
(필요한 속성/메서드만 채택도 가능 - @optional)
3. 모든 타입에서 채택 가능 (밸류타입도 가능)

  • 타입으로 사용가능하므로 활용성이 높아진다.
  • 조합의 장점을 잘 살려서, 보다 나은 구성/재사용성을 높일 수 있다
  • 프로토콜 지향 프로그래밍을 잘 사용하면, 애플이 이미 만들어놓은 데이터타입에도
    채택하여 활용 가능하다
    (소급적 적용 활용 가능)

6) 프로토콜 확장의 적용 제한

protocol Bluetooth {
    func blueOn()
    func blueOff()
}
⬇️
extension Bluetooth where Self: Remote {
    func blueOne() {print("블루투스 켜기")}
    func blueOff() {print("블루투스 끄기")}
}
  • 확장의 적용을 제한
    1) 프로토콜의 확장에서, where절을 통해, 프로토콜 확장의 적용을 제한 가능
    2) 즉, Remote프로토콜을 채택한 타입에만 Bluethooth 프로토콜을 채택 가능하지만
    확장(기본구현)은 제공이 안됨 (무조건 직접 구현해야 함) ㅡ B

0개의 댓글