본문 바로가기
FRONT-END/iOS

Swift 프로토콜(Protocols)

by 랄라J 2023. 8. 1.

클래스와 상속의 단점

  • 하나의 클래스만 상속이 가능함. 즉, 다중 상속 불가능
  • 기본적인 상위클래스의 메모리 구조를 따라갈 수 밖에 없음 (필요하지 않은 속성과 메서드도 상속됨)
  • 클래스(레퍼런스 타입)에서만 가능

 

프로토콜

  • 위 클래스에만 가능한 상속의 여러 문제를 해결하기 위해 나온 문법
  • 실생활에서는 자격증의 개념으로 이해하면 쉬움
  • 구체적인 구현을 하지 않음, 요구사항을 정의함. 구체적인 구현은 프로토콜을 채택한 곳에서 구현
  • class에서는 상속도 프로토콜도 채택하고 싶은 경우 상속하고자 하는 상위클래스를 먼저쓰고 프로토콜을 적으면 됨
  • 상속과 달리 프로토콜은 다중 채택이 가능함으로 ,로 구분해 적으면 됨
protocol CanFly {
    func fly()
}

struct Bird: CanFly {
    func fly() {
    	// 구체적인 구현
    }
}

class Bird2: AClass, CanFly {
    func fly() {
   		// 구체적인 구현
    }
}

 

프로토콜 속성의 요구사항 정의

  • 속성의 뜻에서 var로 선언해야 한다. (let으로 선언할 수 없음)
  • get, set 키워드를 통해 읽기/쓰기 여부 설정
  • 저장 속성/ 계산 속성 모두 구현 가능
  • 최소한의 요구사항을 정확하게 따라서 구현해야함
protocol RemoteMouse {
    var id: String { get } // 채택하는 곳에서는 let 저장속성, var 저장속성, 읽기계산속성 / 읽기, 쓰기 계산속성으로 구현가능
    var name: String { get set } // 저장속성 / 읽기, 쓰기 계산속성
    static var type: String { get set } // 타입 저장 속성 (static) / 타입 계산 속성 (class)
}

struct TV: RemoteMouse {
    let id: String = "113"
    
    // var id: String = "113"
    
    // var id: String {
    //     return "113"
    // }
    
    // var id: String {
    //   get {
    //       return "113"
    //   }
    //   set {}
    // }
    
    var name: String = "텔레비젼"
    
    // var name: String {
    //    get {
    //        return "텔레비젼"
    //    }
    //    set {}
    //}
    
    // 타입 저장 속성은 (상속은 되지만) 재정의 원칙적으로 불가능
    static var type: String = "리모콘"
    
    // 클래스에서 채택 시 타입 계산 속성에서 class/static 키워드로 모두 구현 가능
    // class 키워드는 재정의 가능
    // class var type: String {
    //     get { "리모콘" }
    //     set {}
    // }
}

 

프로토콜 메서드의 요구사항 정의

1) 메서드 요구사항 정의

  • 메서드의 헤드부분(인풋/아웃풋)의 형태만 요구사항으로 정의
  • mutating 키워드 : (구조체로 제한하는 것은 아님) 구조체에서 저장 속성 변경하는 경우, 구조체도 채택 가능하도록 허락하는 키워드
  • 타입 메서드: 클래스에서 채택 시 static/class 키워드 모두 구현 가능
protocol RandomNumber {
    static func reset()
    func random() -> Int
}

 

2) 생성자 요구사항 정의

  • 클래스는 (상속을 고려해야함) 생성자 앞에 required를 붙여야 함 (하위에서 구현을 강제)
  • 아니면 final을 붙여서 상속을 막으면 required 생략가능
  • 클래스에서는 반드시 지정생성자로 구현할 필요없음

 

3) 서브스크립트 요구사항 정의

  • get, set 키워드를 통해서 읽기/쓰기 여부를 설정
  • get 키워드 -> 최소한 읽기 서브스크립트 구현 / 읽기, 쓰기 모두 구현 가능
  • get, set 키워드 -> 반드시 읽기, 쓰기 모두 구현해야함
protocol DataList {
    subscript(idx: Int) -> Int { get }
}

struct DataStructure: DataList {
    subscript (idx: Int) -> Int {
        // get {
        //     return 0
        // }
    	return 0
    }
}

 

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

  • 스위프트는 프로토콜을 일급객체로 취급함
  • 프로토콜은 타입이다
    • 프로토콜을 변수에 할당할 수 있음
    • 함수를 호출할 때, 프로토콜을 파라미터로 전달할 수 있음
    • 함수에서 프로토콜을 반환할 수 있음
  • 타입캐스팅을 통해 구체적인 타입으로 사용가능
    • 프로토콜 타입으로 저장된 인스턴스를 원래의 타입으로 확인 가능
    • 프로토콜 채택 여부 확인 가능

 

프로토콜의 상속

  • 프로토콜 간에 상속이 가능
  • 다중 상속이 가능
  • 실제 프로토콜의 상속까지 구현할 일은 없지만, 애플이 만들어 놓은 체계에서는 많이 쓰이고 있음
  • AnyObject: 클래스 전용 프로토콜
protocol SomeType: AnyObject {
	// 
}

 

프로토콜 합성(Protocol Composition) 문법

  • 프로토콜을 &로 연결해서, 프로토콜 두개를 병합해서 타입으로 사용하는 것 가능
protocol Named {
	var name: String { get }
}

protocol Aged {
	var age: Int { get }
}

func wishHappyBirthday(to celebrator: Named & Aged) {
	// 
}

 

프로토콜의 선택적 요구사항의 구현 (Optional Protocol Requirements)

  • 선택적인 멤버선언하기 @objc 키워드 활용
    • @objc는 스위프트로 작성한 코드를 오브젝티브C 코드에서도 사용할 수 있게해주는 어트리뷰트
    • 프로토콜에서 요구사항 구현 시 반드시 강제해야하는 멤버가 아닌 선택적인 요구사항으로 구현할 때 사용
  • @objc 키워드를 붙인 프로토콜은 클래스 전용 프로토콜
  • 프로토콜 앞에는 @objc 추가 : objectiveC에서 읽을 수 있는 코드라는 의미
  • 멤버 앞에는 @objc optional 추가 : 해당 멤버는 선택적 요구사항으로 바뀜
@objc protocol Remote {
    @objc optional var isOn: Bool { get set }
    func turnOn()
    func turnOff()
    @objc optional func doNetflix()
}

 

어트리뷰트 키워드

  • @available, @objc, @escaping, @IBOutlet, @IBAction 등등
  • 추가적인 정보를 제공하는 키워드
    • 선언에 대한 추가정보 제공 ex) @available
    • 타입에 대한 추가정보 제공  ex) @excaping

 

프로토콜의 확장 (Protocol extension)

  • 프로토콜을 채택한 모든 타입에서, 실제 구현을 계속해서 반복적으로 구현하는 불편을 덜기 위해 프로토콜 확장을 제공해 메서드의 기본 구현을 제공해 코드의 중복을 피하는 장점을 얻음
  • 단, 해당 프로토콜을 채택해 동일한 메서드를 개발자가 직접 구현하는 경우 개발자가 구현한 메서드가 적용됨
  • 프로토콜에 필수 구현으로 적힌 메서드가 아닌 다른 메서드를 확장 시에 정의한다면 해당 프로토콜을 채택 후 동일한 메서드를 개발자가 구현한 경우여도 타입에 따라 해당 메서드가 선택이 되어 실행됨
protocol Remote {
    func turnOn()
    func turnOff()
}

extension Remote {
    func turnOn() { print("리모콘 켜기") }
    func turnOff() { print("리모콘 끄기") }
    
    func doAnotherAction() {
    	print("다른 동작")
    }
}

 

프로토콜 확장의 제한

  • where 절을 통해 프로토콜 확장의 적용을 제한할 수 있음
protocol Remote {
    func blueOn()
    func blueOff()
}

// Remote 프로토콜 타입으로 지정되지 않으면 확장이 적용되지 않음
extension Bluetooth where Self: Remote {
    func blueOn() { print("on") }
    func blueOff() { print("off") }
}

class SmartPhone: Bluetooth {
	// Remote 프로토콜을 채택하지 않아 확장이 적용되지 않음
}

class IPhone: Remote {
	// Remote 프로토콜을 채택해 확장이 적용됨
}

 

추가적으로 알아둬야 할 용어

  • Virtual Table 클래스에서의 메서드 테이블
  • Witness Table 프로토콜의 메서드 테이블
  • Direct dispatch 직접 메모리 주소 삽입
반응형

'FRONT-END > iOS' 카테고리의 다른 글

Swift 중첩 타입 (Nested Types)  (0) 2023.08.02
Swift Method Dispatch  (0) 2023.08.01
Swift 확장(Extensions)  (0) 2023.08.01
Swift 타입캐스팅(Type Casting)  (0) 2023.07.31
클래스의 상속, 초기화, 생성자  (0) 2023.07.31

댓글