공부 연습장 :-)

스위프트 프로토콜 기본, extension과 프로토콜

|

프로토콜

프로토콜은 타입이 만족시킬 인터페이스를 정의할 때 필요하다. 타입이 프로토콜을 만족시킬때 프로토콜을 준수한다고 말한다.

프로토콜은 타입 자체로 사용될 수 있어서 프로토콜 타입을 가지는 변수나 함수 인수 리턴값을 사용할 수 있다.

프로토콜이 필요할때

func printTable(_ data: [[String]], withColumnLabels columnLabels: String...) {
    var firstRow = "|"
    var columnWidths = [Int]()

  ...
  • 이 함수는 행과 열이 몇 개인지 알아야 한다. 인수를 두 개 받아서 반복문으로 table을 출력하는 함수이다. 하지만 프로토콜을 이용해서 TableData를 정의한 프로토콜만 받아도 작동되게 설계할 수 있다.

프로토콜 정의

...
protocol TableDataSource {
  var numberOfRows: Int {get}
  var numberOfColumns: Int {get}

  func label(forColumn column: Int) -> String
  func itemFor(row: Int, column: Int) -> String
}

  • get 문법은 프로퍼티가 읽기용이라는뜻이다.
  • 프로토콜은 타입이 이런 프로퍼티와 메서드만큼은 가져야한다는 최소 요건을 정의한다. TableDataSource 를 준수하는 타입들은 numberOfRows, numberOfColumns프로퍼티, label(), itemFor()메서드를 반드시 구현해야한다.

프로토콜 사용

정의한 프로토콜 TableDataSourse를 준수하는 Department 구조체를 만든다. 위에서 언급했듯이 프로토콜을 추상클래스처럼 사용할 수 있어서, 이 구조체는 TableDataSource타입이 되고 TableDataSourse타입처럼 사용될 수 있다. (ex TableDataSource를 인자로 받는 함수에 Department가 들어갈 수 있음)

struct Person {
    let name: String
    let age: Int
    let yearOfExperience: Int
}

struct Department: TableDataSource { //TableDataSource 프로토콜을 준수함
    let name: String
    var people = [Person]()

    init (name: String) {
        self.name = name
    }

    mutating func add (_ person: Person) {
        people.append(person)
    }

  // 프로토콜에 정의된 numberOfRows와 numberOfColumns는 {get}방식 읽기용 프로퍼티
    var numberOfRows: Int {
        return people.count
    } //var numberOfRows: Int = people.count같음 (이렇게는 사용못함!)

    var numberOfColumns: Int {
        return 3
    }

  // 사용할때 column순서만 정해주면 매칭되는 케이스에서의 문자열을 리턴
    func label(forColumn column: Int) -> String {
        switch column {
        case 0: return "Employee Name"
        case 1: return "Age"
        case 2: return "Years of Experience"
        default: fatalError("Invalid column")
        }
    }

  // Department의 프로퍼티인 Person리스트에서의 person정보를 조회. 행/열 번호만 정해주면 해당 person객체의 속성을 반환  
    func itemFor(row: Int, column: Int) -> String {
        let person = people[row]
        switch column {
        case 0: return person.name
        case 1: return String(person.age)
        case 2: return String(person.yearOfExperience)
        default: fatalError("Invalid column!")
        }
    }
}

extension에 프로토콜적용

extension은 이미 존재하는 클래스나 구조체, 열거형 등의 객체에 새로운 기능을 추가하여 확장할 수 있는 키워드이다.
extension은 라이브러리나 프레임워크에 포함되어 직접 접근할 수 없는 객체라도 확장 구문을 이용하면 제약 없이 새로운 기능을 추가할 수 있다. 또한 extension에 프로토콜을 적용할 수 있다.
프로토콜과 관련된 확장의 특징 중 하나는 기존 객체를 수정하지 않고도 프로토콜을 구현할 수 있다는 것이다.

<StringDictionary의 확장 구문에 프로토콜 구현>

protocol ConvertTarget{

}

extension String: ConvertTarget {

}
extension Dictionary: ConvertTarget {

}
// String과 Dictionary는 프로토콜로 인해 ConvertTarget타입으로 한꺼번에 사용 할 수 있게 되었다.

Comments