Swift문법 - (18)프로토콜 - 2

Youth·2022년 10월 4일
0

swift문법공부

목록 보기
18/27
post-thumbnail

프로토콜(Protocols) - 2

타입으로써의 프로토콜(Protocols as Types)

  • swift는 프로토콜을 일급객체로 취급
    1. 변수에 할당할 수 있음
    2. 함수를 호출할때, 프로토콜을 파라미터로 전달할 수 있음
    3. 함수에서 프로토콜을 반환할 수 있음
protocol Remote {
    func turnOn()
    func turnOff()
}

struct SetTopBox: Remote {
    func turnOn() {
        print("셋톱박스켜기")
    }
    
    func turnOff() {
        print("셋톱박스끄기")
    }
    
    func doNetflix() {
        print("넷플릭스 하기")
    }
}

// Remote타입이기때문에 .doNetflix()는 실행할 수 없다
let sbox: Remote = SetTopBox()
sbox.turnOn()
sbox.turnOff()
//sbox.doNetflix() -> 다운캐스팅하면 사용은 가능할 수 있다

프로토콜 타입 취급의 장점

  • 프로토콜의 타입 취급의 장점 - 1 ⭐️
// 프로토콜의 형식으로 담겨있음
// 프로토콜 타입이 아니면 이 두개는 하나의 배열에 담을 수없음
// Any로 담으면 죄다 다운캐스팅 해서 사용해야함
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

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

// 프로토콜 타입으로 저장된 인스턴스가 더 구체적인 타입인지 확인 가능
electronic[0] is TV
electronic[1] is SetTopBox
  1. as 연산자
// 업캐스팅(as)
let newBox = sbox as Remote
newBox.turnOn()
newBox.turnOff()

// 다운캐스팅(as?/as!) -> 프로토콜: 범용, 채택한 객체: 구체적
// electronic[1]은 Remote타입이기때문에 인스턴스화하려면 다운캐스팅해야함
// 다운캐스팅을 하게되면 SetTopBox 객체기때문에 .doNetflix()가능
let sbox2: SetTopBox? = electronic[1] as? SetTopBox
sbox2?.doNetflix()

프로토콜의 상속

  • 프로토콜도 상속이 가능하다
  • 프로토콜은 다중상속이 가능하다

B프로토콜이 A라는 프로토콜을상속하고 있음

→ B프로토콜을 채택하면 A,B의 요구사항을 모두 구현해야함

C프로토콜이 A와 B라는 프로토콜을 다중상속하고 있음

→ C프로토콜을 채택하면 ABC의 요구사항을 모두 구현해야함

클래스 전용 프로토콜 (AnyObject)

// AnyObject을 프로토콜이 채택하면 이 프로토콜은 class에서만 사용이 가능하다
protocol SomeProtocol: AnyObject {      
    func doSomething()
}

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

  • 프로토콜을 합성하여 임시타입으로 활용 가능 ⭐️
protocol Named {
    var name: String { get }
}

protocol Aged {
    var age: Int { get }
}
  • 하나의 타입에서 여러개의 프로토콜을 채택하는 것 당연히 가능
struct Person: Named, Aged {
    var name: String
    var age: Int
}
  • 프로토콜을 두개를 병합해서 사용 하는 문법(&로 연결)
// 임시적인 타입으로 인식
// Named라는 프로토콜 Aged라는 프로토콜을 모두 채택한 인스턴스만 사용 가능
func wishHappyBirthday(to celebrator: Named & Aged) {   
    print("생일축하해, \(celebrator.name),
					 넌 이제 \(celebrator.age)살이 되었구나!")
}

let birthdayPerson = Person(name: "홍길동", age: 20)
wishHappyBirthday(to: birthdayPerson)

// 임시적인 타입으로 저장 (두개의 프로토콜을 모두 채택한 타입만 저장 가능)
let whoIsThis: Named & Aged = birthdayPerson      

선택적 요구사항의 구현

  • 어튜리뷰트 키워드
  • @available, @objc, @escaping, @IBOutlet, @IBAction 등등

선택적인(구현해도 되고 안해도 되는) 멤버선언하기

  • @objc는 스위프트로 작성한 코드를 오브젝티브C 코드에서도 사용할 수 있게 해주는 어트리뷰트
  • 프로토콜에서 요구사항 구현시, 반드시 강제하는 멤버가 아니라 선택적인 요구사항으로 구현할때 사용
// 프로토콜 앞에는 "@objc"추가
// 선택적구현이 존재하는 프로토콜이라는 뜻
@objc protocol Remote {
		// 멤버 앞에는 "@objc optional"을 모두 추가
    @objc optional var isOn: Bool { get set }
    func turnOn()
    func turnOff()
    @objc optional func doNeflix()
}

class TV: Remote {
    var isOn = false    
    func turnOn() {}    
    func turnOff() {}
}

프로토콜의 확장(Protocol Extension)

  • 기본(디폴트) 구현 제공 ⭐️
  • 프로토콜의 확장 - 프로토콜 지향 프로그래밍 ⭐️
  • 아래와 같은상황이면(프로토콜로 선언한 함수실행문이 동일) → 불편함
protocol Remote {
    func turnOn()
    func turnOff()
}

struct Aircon1: Remote {
    func turnOn() { print("리모콘 켜기") }
    func turnOff() { print("리모콘 끄기") }
}

struct Aircon2: Remote {
    func turnOn() { print("리모콘 켜기") }
    func turnOff() { print("리모콘 끄기") }
}
💡 프로토콜을 채택한 모든 타입에서, 실제 구현을 계속적으로 반복해야하는 불편함을 덜기 위해"프로토콜 확장"을 제공해서 메서드의 디폴트 구현을 제공함
extension Remote {
		// 만약에 사용자가 따로 구현을하면 그 구현된 함수가 우선으로 채택 
    func turnOn() { print("리모콘 켜기") }    
    func turnOff() { print("리모콘 끄기") }  
    
		// 타입에 따른 선택
		// 우선순위가 있는게 아니라 타입이 프로토콜이면 여기있는함수 실행
		// 타입이 인스턴스면 인스턴스에 있는 doAnotherAction()이 실행
    func doAnotherAction() {              
        print("리모콘 또 다른 동작")           
    }
}

→ 프로토콜 타입이라면 WitnessTable 클래스타입이라면 VirtualTable

프로토콜 확장 - 형식을 제한 가능

  • 프로토콜 확장에서 where절을 통해, 프로토콜의 확장의 적용을 제한 가능
  • "특정 프로토콜"을 채택한 타입에만 프로토콜 확장이 적용되도록 제한
    where Self: 특정프로토콜
protocol Bluetooth {
    func blueOn()
    func blueOff()
}

// 특정 프로토콜을 채택한 타입에만 프로토콜 확장이 적용되도록 제한
// 본 확장의 적용을 제한시키는 것 가능 (구체적 구현의 적용범위를 제한)
// Remote 프로토콜을 채택한 타입만 확장 적용 가능
extension Bluetooth where Self: Remote {   
    func blueOn() { print("블루투스 켜기") }
    func blueOff() { print("블루투스 끄기") }
}

// Remote프로토콜을 채택한 타입만 Bluetooth 확장이 적용됨
// Remote프로토콜을 채택하지 않으면 확장이 적용되지 않기 때문에 직접 구현 해야함
class SmartPhone: Remote, Bluetooth { }
profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료

0개의 댓글