티스토리 뷰

Swift

Swift [Protocol 2부]

이도형 2023. 2. 27. 01:10

INDEX

1.  프로토콜의 확장(Extension)

2.  프로토콜 확장의 제한

3.선택적인 요구사항(Optional Protocols Requirements)

4. 프로토콜 지향 프로그래밍이란?

5. 마무리

 

 

 

1.  프로토콜의 확장(Extension)

프로토콜의 확장(Protocol Extension)

프로토콜의 확장에서는, 프로토콜의 선언에서 요구하는 

필수요구 사항 메서드의 기본적인 디폴트 구현 제공

(귀찮은 방식으로) 프로토콜을 채택한 모든 타입에서,

동일 구현을 계속적으로 반복해야하는 불편함을 덜기 위해

"프로토콜 확장"을 제공해서 메서드의 디폴트 구현을 제공함 (코드의 중복을 피한다.)

protocol Remote {
    func turnOn()
    func turnOff()
}


// 채택 ===> 실제구현해야함(여러타입에서 채택한다면 반복적으로 구현해야하는 점이 불편)

class TV1: Remote {
    //func turnOn() { print("리모콘 켜기") }
    //func turnOff() { print("리모콘 끄기") }
}


// 채택 ===> 실제구현해야함(여러타입에서 채택한다면 반복적으로 구현해야하는 점이 불편)

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

프로토콜의 확장 사용예제(우선순위에 집중)

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

class Ipad: Remote {
    func turnOn() { print("아이패드 켜기") }
    func doAnotherAction() { print("아이패드 다른 동작") }
}

var x = Ipad()
x.turnOn() // { print("아이패드 켜기") }
x.turnOff() // { print("리모콘 끄기") } 구현하지 않아도 확장으로 자동 정의
x.doAnotherAction() // { print("아이패드 다른 동작") }

let ipad: Ipad = Ipad()
ipad.turnOn()           // 아이패드 켜기
ipad.turnOff()          // 리모콘 끄기
ipad.doAnotherAction()  // 아이패드 다른 동작

let ipad2: Remote = Ipad()
ipad2.turnOn()          // 아이패드 켜기
ipad2.turnOff()         // 리모콘 끄기 
ipad2.doAnotherAction() // 리모콘 또 다른 동작

구조체에서도 똑같이 가능

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

    func doAnotherAction() { print("스마트폰 다른 동작") }
}

var iphone: SmartPhone = SmartPhone()
iphone.turnOn() // 스마트폰 켜기          
iphone.turnOff() // 리모콘 끄기            
iphone.doAnotherAction() // 스마트폰 다른 동작

var iphone2: Remote = SmartPhone()
iphone2.turnOn() //스마트폰 켜기         
iphone2.turnOff() //리모콘 끄기           
iphone2.doAnotherAction() //리모콘 또 다른 동작

 

2.  프로토콜 확장의 제한

 

프로토콜 확장에서 where절을 통해, 프로토콜의 확장의 적용을 제한 가능

 1. "특정 프로토콜"을 채택한 타입에만 프로토콜 확장이 적용되도록 제한

    where Self: 특정프로토콜

 2. 특정 프로토콜을 채택하지 않으면, 프로토콜의 확장이 적용되지 않기 때문에

   (확장이 없는 것과 동일하게) 메서드를 직접구현 해야함

 

protocol Remote {
    func turnOn()        // 요구사항
    func turnOff()       // 요구사항
}

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

protocol Bluetooth {
    func blueOn()
    func blueOff()
}

// (Remote 프로토콜을 채택한 타입만 확장 적용 가능)
extension Bluetooth where Self: Remote {   //Remote프로토콜 채택 필수
    func blueOn() { print("블루투스 켜기") }
    func blueOff() { print("블루투스 끄기") }
}


// Remote프로토콜을 채택한 타입만 Bluetooth 확장이 적용됨
// Remote프로토콜을 채택하지 않으면 ===> 확장이 적용되지 않기 때문에 직접 구현 해야함
class SmartPhone: Remote, Bluetooth { }


let sphone = SmartPhone()
sphone.turnOn()           // 리모콘 켜기
sphone.turnOff()          // 리모콘 끄기
sphone.blueOn()           // 블루투스 켜기
sphone.blueOff()          // 블루투스 끄기

 

 

3.선택적인 요구사항(Optional Protocols Requirements)

 

선택적인 프로토콜 요구사항이란 프로토콜을 채택한 곳에서

구현해도되고 안해도되는 프로토콜 내부에 옵셔널 멤버를 선언할 수 있음을 뜻한다.

이 개념을 이해하기 위해서는 먼저 어트리뷰트 키워드에 대해 알아야한다.

 

어트리뷰트 키워드(Attributes)

컴파일러에게 알려주는 특별한 신호이자, 추가적인 정보를 제공하는 키워드로 2가지 종류가 있음 

1) 선언에 대한 추가정보 제공

2) 타입에 대한 추가정보 제공

ex) @available, @objc, @escaping, @IBOutlet, @IBAction 등등

 

실제 사용 예시

@available(iOS 10.0, macOS 10.12, *)
class SomeType {      // "SomeType" 선언은 iOS 10.0 버전이상에서만 읽을 수 있음
    
}

 

이정도만 간략하게 설명하고 어트리뷰트 키워드 @objc에 대해 중점적으로 설명한다.

 

프로토콜에서 어트리뷰트 키워드 @objc를 통해 Optional멤버를 선언할 수 있다.

@objc는 스위프트로 작성한 코드를 오브젝티브C 코드에서도 사용할 수 있게 해주는 어트리뷰트로

프로토콜에서 요구사항 구현시, 반드시 강제하는 멤버가 아니라

선택적인 요구사항(옵셔널)으로 구현할때 사용한다

선택적 멤버를 선언한(@objc) 프로토콜 구현시

오브젝티브-C에 해당하는 클래스 전용 프로토콜이므로

구조체, 열거형에서는 채용 불가하다(class-only)

⭐️@objc를 선언한 프로토콜은 AnyObject를 자동으로 채택한다고 보면 편하다⭐️

 

1. 프로토콜 앞에는 "@objc"추가 

2. 옵셔널 선언하고 싶은 멤버 앞에는 "@objc optional" 추가

@objc protocol Remote: AnyObject {
    @objc optional var isOn: Bool { get set }
    func turnOn()
    func turnOff()
    @objc optional func doNeflix()
}

//❗️구조체에서는 불가능. @objc가 있으므로 클래스만 채택 가능
//struct TV2: Remote {
//    var isOn = true
//    
//    func turnOn() {}
//    
//    func turnOff() {}
//    
//    func doNeflix() {
//        print("Netflix")
//    }
//}

class TV: Remote {
    var isOn = true
    
    func turnOn() {}
    
    func turnOff() {}
    
    func doNeflix() {
        print("Netflix")
    }
}

//@objc는 타입이 Optional인 만큼 채택하는 곳에서도 옵셔널체이닝 문법으로 접근해야함 ?.

let tv1: TV = TV()
print(tv1.isOn)   // Bool타입

var tv2: Remote = TV()
print(tv2.isOn) //Optional(true)
// Bool? 타입 (선택적 구현 사항이기 때문에 해당 멤버가 없으면 ===> nil로 반환)

tv2 = tv2 as! TV   // 강제 타입캐스팅
tv2.doNeflix?() //Netflix 
//(선택적으로 선언(@objc)했기 때문에, 함수가 없을 수도 있음 => 옵셔널 체이닝)

 

4. 프로토콜 지향 프로그래밍이란?

프로토콜 지향 프로그래밍(protocol oriented programming)?


스위프트에서는 "문법적으로" 프로토콜에 다양하고, 디테일한 기능을 제공해서,

 (다른 언어와 비교해서) 프로토콜을 훨씬 더 다양한 방식으로 활용할 수 있도록 하는

 "프로토콜 지향" 프로그래밍을 내세우고 있음

(다만, 객체지향 프로그래밍(OOP)이나 함수형 프로그래밍과 같은 수준의

새로운 또 다른 차원의 패러다임을 제공해주는 개념은 아님)

프로토콜 지향 프로그래밍의 장점은

(1) 다중 상속이 불가능한 클래스의 단점을 극복하여, 여러개의 프로토콜을 채택가능하게 하여

 다중 상속과 유사한 방식으로 활용 가능

(2) 상위 클래스의 모든 저장속성/메서드를 반드시 상속하게 되는 상속의 단점을 극복하게하여

특정한 속성과 메서드의 요구사항이 없어져, 보다 유연한 설계가 가능함

(3) 클래스 뿐만 아닌, 구조체,열거형(값타입)에서도 상속과 같은 효과를 누릴 수 있음

(4) 프로토콜 확장에서, 메서드에 대한 디폴트 구현을 제공해서, 채택하는 타입에서 보다 편리하게 사용 가능

(5) 프로토콜 자체가 타입 자체로 인식이 되므로, 변수에 담을 수 있는 등 활용성이 높아짐

(6) 프로토콜을 사용해서, 이미 구현된 타입들을 확장하여 소급 적용의 효과를 누릴 수 있음

 

5. 마무리

 

프로토콜은 다중상속과 확장이 가능하고 값타입에서도 채택이 가능하다는 점에서 OOP와는 차별화된 장점을 지녔다고 볼 수 있다. 또한 @objc키워드를 통해 선택적인 요구사항도 멤버로 선언할 수 있었고, 프로토콜 지향 프로그래밍이라는 중요한 개념에 대해서도 알아볼 수 있었다.

총 2부에 걸쳐 프로토콜의 기본적인 부분은 모두 다루었다고 볼 수 있다. 하지만 프로토콜은 심화적으로 공부할 부분이 더 많다. 프로토콜의 확장을 공부하면서는 Method Dispatch의 이해가 동반되어야 내부적으로 메모리상 어떻게 메서드가 실행되는지 알 수 있고 Equtable, Comparable, Hashable등의 특정 프로토콜에 대해서도 공부할 필요가 있으며 실제로 앱을만들때 활용되는 델리게이트패턴도 프로토콜의 이해가 동반되어야 한다. 이번에는 가장 기본적이고 쉬운 부분들만 다루었고,

심화적인 부분들에 대해서는 다음에 완전한 이해가 되었을때 포스팅하고자 한다.

 

출처

Inflearn Allen님의 강의 노트

Swift 공식문서

야곰님의 스위프트 프로그래밍 3

 

반응형

'Swift' 카테고리의 다른 글

Swift [ARC- strong, memory leak, weak, unowned]  (0) 2023.03.02
Swift [Closure 1부]  (0) 2023.03.01
Swift [Protocol 1부]  (0) 2023.02.26
Swift [Extension]  (0) 2023.02.26
Swift [Any, AnyObject]  (0) 2023.02.25
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함