Swift에는 클래스를 확장하는데 도움이 되는 기능이 포함되어 있다. 프로토콜은 메소드의 구현을 의무화할수 있기에, 확장(extension)는 메소드를 나중에 추가한다. 이 두개를 합한 기술을 설명한다.


프로토콜(Protocol)

Swift는 클래스에 포함된 메소드를 호출해 처리한다. 어떤 오브젝트를 처리할 때 "그 안에 어떤 메소드가 준비되어 있는가"를 생각하게 된다.

여기에 완전히 다른 클래스 객체가 여러개가 있었다고 하자. 그것들 모와서 어떠한 처리할 때 "모두에 공통된 메소드"를 제공하게 되면 매우 도움이 될 것이다.

그렇지만, 클래스가 다른 경우 각각에 같은 메소드가 준비되어 있을 지에 대해 전혀 알 수가 없다. 각각의 클래스가 상속 관계에 있으면, 슈퍼 클래스에 메소드를 준비해 두는 것으로 어떻게든 되겠지만, 상속 관계가 없는 클래스에 대해서는 같은 메소드가 있다는 것을 보증할 수 없다. "프로그래머가 신경을 써서 공통의 것을 준비해 둔다"가 아니라, "반드시 이 메소드가 준비되어 있다"고 확신할 수 있는가 라는 것이다.

이것을 가능하게 하는 것이 "프로토콜"이다. 프로토콜은 속성과 메소드 선언만 작성한 클래스와 같은 모양을 하고 있다. 작성 방법을 정리하면 이렇게 될 것이다.

protocol 프로토콜 이름 {
    ...... 속성과 메소드의 선언 ......
}

메소드는 {} 안에 구현 부분은 필요 없다. 단순히 "func ○○ (✕✕) -> △△;"라고 선언 부분만 있으면 된다.

이렇게 작성된 프로토콜은 클래스에 통합 이용된다. 이것은 클래스 상속과 기본적으로 유사하게 작성을 한다.

class 클래스명 : 프로토콜명 {...}

클래스의 상속을 하고 있는 경우는 "클래스명 : 슈퍼 클래스 이름, 프로토콜 이름 ......"와 같이, 상속 클래스명 뒤에 쉼표로 구분하여 작성한다. 또한 프로토콜은 다중를 설정할 수 있기에 이런 경우에도 각각을 쉼표로 구분하여 작성해 주면 된다.

이렇게 프로토콜을 설정된 클래스는 프로토콜에 포함된 모든 메소드를 구현해야 한다. 구현하지 않으면 오류가 발생하여 실행할 수 없다. 여러 클래스에 프로토콜을 설정하면, 그 클래스에 반드시 프로토콜의 메소드가 구현되어야 한다는 것이다.

 

그러면 실제로 프로토콜을 사용한 간단한 예제를 만들어 보자.

protocol MyProtocol {
    func printData ()
}

class PersonData: MyProtocol {
    var name:String = ""
    
    init(name:String) {
        self.name = name
    }
    
    func printData() {
        print("[name: \(name).]")
    }
}

class MemoData: MyProtocol {
    var content:String = ""
    
    init(content:String) {
        self.content = content
    }
    
    func printData() {
        print(content)
    }
}

var data:[MyProtocol] = []
data.append(PersonData(name: "kimkc"))
data.append(MemoData(content: "아침 9시부터 회의"))
for obj in data {
    obj.printData()
}

여기에서는 MyProtocol라는 프로토콜을 정의하고 거기에 데이터를 출력하는 printData라는 메소드가 작성되어 있다. 그리고 이 프로토콜을 구현한 클래스로 "PersonData "과 "MemoData"를 작성했다. PersonData는 개인 정보를 관리하는 것이고, MemoData 메모를 보관하는 것이다. 어느 쪽도 전혀 상속 관계도 없고, 사용할 수 있는 속성도 다르다.

클래스 정의 후에 이를 이용한 간단한 처리가 있다. PersonData과 MemoData를 배열로 모와서 반복적으로 내용을 출력한다.

여기에서는 MyProtocol의 배열을 만들고, 거기에 PersonData과 MemoData을 보관하고 있다. 프로토콜은 클래스과 같이 유형(타입)으로 지정할 수 있다.

MyProtocol 배열에는 MyProtocol를 구현한 클래스의 인스턴스라면 어떤 것이라도 넣을 수 있다. 그리고 MyProtocol에 캐스팅된 인스턴스에서 for문으로 printData를 호출하고 있다.

이 처럼, '프로토콜의 인스턴스로 캐스팅하고 메서드를 호출'이라는 방식으로 PersonData과 MemoData는 상속 관계도 아무것도 전혀 무관한 클래스를 한꺼번에 처리 할 수 있게 되는 것이다.



확장(Extension)

프로토콜은 클래스 메소드의 구현을 의무화하는 것이었지만, 확장 (Extension)는 클래스에 직접 메소드를 덧붙이는 기능이다. 이는 사용법도 매우 간단하고, 아래와 같이 작성한다.

extention 클래스 이름 {
    ...... 추가 내용 ......
}

"클래스명"에는 기능을 추가하는 클래스명을 지정한다. 정말 간단하다.

추가할 수 있는 것은 메소드뿐만 아니라, 속성도 가능하다. 단, 속성의 경우 "Computed 속성"만 추가할 수 있다. 메소드처럼 get/set 처리를 준비하는 유형(타입)의 속성이다.

이 확장은 자작 클래스뿐만 아니라 Swift 시스템 라이브러리로 제공되는 클래스와 iOS 프레임워크의 클래스 등도 확장할 수 있다.

예를 들어, Int 클래스를 확장하여 1부터 주어진 숫자의 합계를 얻는 메소드 getTotal을 추가해 보도록 하자.

extension Int {
    func getTotal()->Int {
        var total:Int = 0
        for i in 1...self {
            total += i
        }
        return total
    }
}

var num = 1234
print(num.getTotal())

여기에서는 extension Int와 같이 하여 Int 클래스를 확장하고 있다. 이렇게 함으로써, Int 형의 값 모두에 getTotal 메소드가 추가 되었다. var num = 1234와 같이 일반적으로 작성된 Int 변수도 getTotal를 사용할 수 있게 된 것이다. 예를 들어, "100까지의 합을 알고 싶다"고 한다면 단지 "100.getTotal()"실행하면 된다.

Int와 String 같이, Swift의 가장 기본적인 것에 대해서도 이렇게 확장에서 쉽게 기능을 추가 할 수 있다.





프로토콜과 확장을 결합

이 프로토콜과 확장을 결합하면, 재미있는 것을 할 수 있다.

확장은 메소드와 속성뿐만 아니라 프로토콜을 추가하는 것도 가능하다. 이는 이미 있는 여러 클래스에 공통된 기능을 구현하여 함께 처리할 수도 있다.

이것도 실제 사용 예제를 살펴 보자.

protocol MyDataPrintable {
    func printData ()
}

extension String: MyDataPrintable {
    func printData() {
        print("문자열:\(self).")
    }
}

extension Int: MyDataPrintable {
    func printData() {
        print("숫자:\(self).")
    }
}

var str = "Hello"
var num = 12345
str.printData()
num.printData()

위에 예제는 MyDataPrintable라는 프로토콜을 작성하고, 거기에 printData 메소드가 작성되어 있다. 그리고 확장을 사용하여 이와 같이 String과 Int에 MyDataPrintable 프로토콜을 추가한다.

extension String: MyDataPrintable {...}
extension Int: MyDataPrintable {...}

이렇게 하면 모든 String 값이나 Int 값이 MyDataPrintable로 취급할 수 있게 된다. 즉, String과 Int를 같은 프로토콜 클래스로 동일하게 모와서 처리할 수 있게 되는 것이다.

이와 같이, 확장 및 프로토콜은 단순히 자신의 클래스를 강화할 뿐만 아니라 Swift에 포함되어 있는 모든 클래스에 자신의 확장을 실행 시킬 수 있고, 나름대로 커스텀마이징 하는 것을 허용한다. 꽤 강력한 기능이므로 꼭 기억해 두도록 하자.

'Swift' 카테고리의 다른 글

[Swift] 프로토콜 및 확장  (0) 2017.12.10
[Swift] 함수 리터럴 및 클로저  (0) 2017.12.10
[Switf] 구조체, 열거형, 튜플  (0) 2017.12.09
[Swift] 배열과 사전  (0) 2017.12.09
[Swift] 클래스 기본  (0) 2017.12.09
[Swift] 함수  (0) 2017.12.09

+ Recent posts