seong_hye, the developer

Swift) 문법 정리 - 프로토콜 본문

IOS

Swift) 문법 정리 - 프로토콜

seong_hye 2022. 7. 12.

프로토콜

규약 / 협약

최소한의 요구사항만 가지면 사용 가능

특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항등의 청사진

protocol MyProtocol {
	func doing() -> Int	// 요구사항
}

프로토콜이 필요한 이유?

상속의 경우 하나의 클래스만 상속이 가능( 다중 상속 불가능)

상속의 경우 기본적인 상위클래스의 메모리 구조를 따라갈 수 밖에 없음

(필요없는 속성/메서드도 상속됨)

class Bird {
    var isFemale = true
    
    func layEgg() {
        if isFemale {
            print("새가 알을 낳는다.")
        }
    }
    
    func fly() {
        print("새가 하늘로 날아간다.")
    }
}

class Eagle: Bird {
    
    // isFamale
    // layEgg()
    // fly()
    
    func soar() {
        print("공중으로 치솟아 난다.")
    }
}

class Penguin: Bird {
    
    // isFamale
    // layEgg()
    // fly()       // 상속 구조에서는 펭귄이 어쩔 수 없이 날개됨 ⭐️
    
    func swim() {
        print("헤엄친다.")
    }
}

// struct가 될 수도 없고(클래스로만 구현가능), 무조건 Bird를 상속해야만 함
class Airplane: Bird {
    
    // isFamale
    // layEgg()         // 상속 구조에서는 비행기가 알을 낳게됨 ⭐️
    
    override func fly() {
        print("비행기가 엔진을 사용해서 날아간다")
    }
}

프로토콜이 클래스의 단점을 보완

// "fly()"라는 기능을 따로 분리해 내기

protocol CanFly {
    func fly()      // 구체적인 구현은 하지 않음 ===> 구체적인 구현은 자격증을 채택한 곳에서
}

class Bird1 {
    
    var isFemale = true
    
    func layEgg() {
        if isFemale {
            print("새가 알을 낳는다.")
        }
    }

}

class Eagle1: Bird1, CanFly {    // "CanFly" 자격증을 채택
    
    // isFemale
    // layEgg()
    
    func fly() {
        print("독수리가 하늘로 날라올라 간다.")
    }
    
    func soar() {
        print("공중으로 활공한다.")
    }
}

class Penguin1: Bird1 {
    
    // isFemale
    // layEgg()
    
    func swim() {
        print("물 속을 헤엄칠 수 있다.")
    }
}

// 구조체에서 채택도 가능
struct Airplane1: CanFly {
    func fly() {
        print("비행기가 날아간다")
    }
}

프로토콜은 여러개의 프로토콜을 채택할 수 있음

클래스에서 상속이 있는 경우 상위 클래스를 먼저 선언 후 그 뒤에 프로토콜을 채택 선언함

class Student: Person, AProtocol, BProtocol {
	
    // AProtocol, BProtocol이 원하는 내용 구현해야함

}

프로토콜 속성의 요구사항

인스턴스 속성 요구사항

최소한의 요구사항을 지정

속성의 뜻에서 let, var로 선언 (set 키워드 사용시 let으로 선언할 수 없음)

 get, set 키워드를 통해서 읽기/쓰기 여부를 설정 (최소한의 요구사항일뿐)

저장 속성/계산 속성으로 모두 구현 가능

protocol RemoteMouse {
    
    var id: String { get }                // ===> let 저장속성 / var 저장속성 / 읽기계산속성 / 읽기,쓰기 계산속성
    
    var name: String { get set }          // ===> var 저장속성 / 읽기,쓰기 계산속성

    static var type: String { get set }   // ===> 타입 저장 속성 (static)
                                          // ===> 타입 계산 속성 (class)
}

struct TV: RemoteMouse {
    
    var id: String = "456"
    
    var name: String = "삼성티비"
    
    static var type: String = "리모콘"
}

 

타입 속성 요구사항

최소한의 요구사항을 지정

저장 타입 속성/계산 타입 속성으로 모두 구현 가능

채택 시 저장 타입 속성에서 static키워드로만 구현 가능 (저장 속성 재정의 불가 원칙)

클래스에서 채택시에만 계산 타입 속성에서 static / class 키워드로 모두 구현 가능

// 1) 저장 타입 속성으로 구현

class SmartPhone: RemoteMouse {
    var id: String {
        return "777"
    }
    
    var name: String {
        get { "아이폰" }
        set { }
    }
    
    static var type: String = "리모콘"     // 타입 저장 속성은 (상속은 되지만) 재정의 원칙적 불가능
}


// 2) 계산 타입 속성으로 구현

class Ipad: RemoteMouse {
    var id: String = "777"
    
    var name: String = "아이패드"
    
    class var type: String {       // 타입 계산 속성은 재정의 가능 (class키워드 가능)
        get { "리모콘" }
        set { }
    }
}

프로토콜 메서드 요구사항

 메서드의 헤드부분(인풋/아웃풋)의 형태만 요구사항으로 정의

 mutating 키워드: 구조체에서 저장 속성 변경하는 경우,

구조체도 채택 가능하도록 허락하는 키워드

 타입 메서드로 제한 하려면, static키워드만 붙이면 됨

   (채택해서 구현하는 쪽에서 static / class 키워드 모두 사용 가능)

// 1) 정의
protocol RandomNumber {
    static func reset()         // 최소한 타입 메서드가 되야함 (class로 구현해서 재정의를 허용하는 것도 가능)
    func random() -> Int
    //mutating func doSomething()
}

// 2) 채택 / 3) 구현
class Number: RandomNumber {
    
    static func reset() {
        print("다시 셋팅")
    }
    
    func random() -> Int {
        return Int.random(in: 1...100)
    }
}

관습적으로 프로토콜의 채택은 확장(Extension)에서 구현하는 것을 권장함

// 관습적으로 본체보다는 확장에서, 채택 구현 (코드의 깔끔한 정리 가능)
extension Person: Certificate {
    func doSomething() {
        print("Do something")
    }
}

프로토콜의 확장

프로토콜을 채택한 모든 타입에서, 실제 구현을 계속적으로 반복해야하는 불편함을 덜기 위해

메서드의 디폴트 구현을 제공함 (코드의 중복을 피할 수 있음)

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

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

 

프로토콜 확장의 적용 제한

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

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

    ex) where Self: 특정프로토콜

 

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

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

extension Bluetooth where Self: Remote { 
    func blueOn() { print("블루투스 켜기") }
    func blueOff() { print("블루투스 끄기") }
}

class SmartPhone: Remote, Bluetooth {
    
}

참고자료)

https://www.udemy.com/course/ios-13-app-development-bootcamp/

https://zrr.kr/qNHN

 

앨런 Swift문법 마스터 스쿨 (온라인 BootCamp - 2개월과정) 강의 - 인프런

Swift문법을 제대로 이해, 활용해보고자 하는 철학을 바탕으로 과정이 설계되었습니다. 코딩에 대해 1도 모르는 비전공자를 시작으로 네카라쿠배에 입사할 수 있는 초고급 수준까지 올리는 것을

www.inflearn.com

 

Comments