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() -> 다운캐스팅하면 사용은 가능할 수 있다
// 프로토콜의 형식으로 담겨있음
// 프로토콜 타입이 아니면 이 두개는 하나의 배열에 담을 수없음
// Any로 담으면 죄다 다운캐스팅 해서 사용해야함
let electronic: [Remote] = [tv, sbox]
// 켜기, 끄기 기능만 사용하니 타입캐스팅을 쓸 필요도 없음
// 다만, 프로토콜에 있는 멤버만 사용가능
for item in electronic {
item.turnOn()
}
func turnOnSomeElectronics(item: Remote) {
item.turnOn()
}
turnOnSomeElectronics(item: tv)
turnOnSomeElectronics(item: sbox)
// 특정타입이 프로토콜을 채택하고 있는지 확인
tv is Remote
sbox is Remote
// 프로토콜 타입으로 저장된 인스턴스가 더 구체적인 타입인지 확인 가능
electronic[0] is TV
electronic[1] is SetTopBox
// 업캐스팅(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을 프로토콜이 채택하면 이 프로토콜은 class에서만 사용이 가능하다
protocol SomeProtocol: AnyObject {
func doSomething()
}
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
// 프로토콜 앞에는 "@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 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
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 { }