이 포스팅은 Swift 시리즈 20 편 중 10 번째 글 입니다.
목차
익스텐션
- 강력한 기능!
- 구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있음
- 기능을 추가하려는 타입의 구현된 소스 코드를 알지 못하거나, 볼 수 없어도
- 타입만 알고있다면 타입의 기능을 확장할 수 있음!
추가 가능 기능
- 연산 타입 프로퍼티
- 연산 인스턴스 프로퍼티
- 타입 메서드
- 인스턴스 메서드
- 이니셜라이저
- 서브스크립트
- 중첩 타입
- 특정 프로토콜을 준수할 수 있도록 기능 추가
다만, 재정의는 불가하다.
상속 vs 익스텐션
비슷해보이나 굉장히 다르다. 상속은 클래스 타입에서만 가능하다. 익스텐션은 구조체, 클래스, 프로토콜 등에 적용을 할 수 있다. 또 클래스의 상속은 특정 타입을 물려받아 하나의 새로운 타입을 정의하고 추가기능을 구현하는 수직확장이지만, 익스텐션은 기존의 타입에 기능을 추가하는 수평 확장이다. 상속을 받으면 기존 기능을 재정의할 수 있지만, 익스텐션은 재정의가 불가하다.
상속 | 익스텐션 | |
---|---|---|
확장 | 수직 | 수평 |
사용 | 클래스 타입 | 클래스, 구조체 프로토콜, 제네릭 등 모든 타입 |
재정의 | 가능 | 불가능 |
익스텐션의 활용
- 기존 타입에 기능을 추가하는 방법도 있음
- 하지만 외부 라이브러리나 프레임워크를 가져다 썼다면 원본 소스를 수정하지 못함
- 외부에서 가져온 타입에 원하는 기능을 추가하고자 할 때, 익스텐션을 사용함
- 상속을 받지 않아도 되고, 구조체, 열거형에도 추가가 가능하여 매우 편리함
- 모든 타입에 적용이 가능함
- 모든 타입 : 구조체, 열거형, 클래스, 프로토콜, 제네릭 타입
- 프로토콜과 함께 사용하면 굉장히 강력함.
Protocol Oriented Programming
에 대해 알아볼 것!
정의
extension
키워드 사용하여 정의
extension 확장타입이름 {
/* define */
}
- 익스텐션을 또 이런 기능도 있다.
- 기존의 존재하는 타입이 추가적으로 다른 프로토콜을 채택할 수 있도록 확장할 수 있다.
- 내부 소스코드로 안가고, 추가로 확장해서 사용하는 것
extension 확장타입이름: 프로토콜1, 프로토콜2, 프로토콜3 {
/* define */
}
- 스위프트 라이브러리에는 익스텐션이 굉장히 많이 사용된다.
- Double 타입에는 수많은 프로퍼티와 메서드, 생성자가 정의되어 있다.
- 그러면 당연히 수많은 프로토콜을 채택하고 있지 않을까? 준수할게 많겠지.
- 그런데 실제 정의를 가보면 모든 것이 다 정의되어 있지는 않다.
- 스위프트 표준 라이브러리 타입의 기능은 대부분 익스텐션으로 구현되어 있다.
구현
하나씩 추가해보자.
연산 프로퍼티 추가
- 아래 익스텐션은 Int 타입에 두 개의 연산 프로퍼티를 추가한 것
- Int 타입의 인스턴스가 홀수인지 짝수 인지 판별하여 Bool 타입으로 알려주는 연산 프로퍼티임
- 이렇게 추가해주면 Int 타입의 모든 인스턴스에서도 사용가능
- static 키워드로 타입 연산 프로퍼티도 추가할 수 있음
extension Int {
var isEven: Bool {
print(self)
return self % 2 == 0
}
var isOdd: Bool {
return self % 2 == 1
}
}
print(1.isEven) // false
print(2.isEven) // true
print(1.isOdd) // true
print(2.isOdd) // false
var number: Int = 3
print(number.isEven) // false
print(number.isOdd) // true
number = 2
print(number.isEven) // true
print(number.isOdd) // false
신기한게 self로 접근하는데, 이부분 정의를 보아도 아직 해결하지 못했다.
메서드 추가
- Int 타입에 인스턴스 메서드인 multiply 메서드를 추가해보자.
- 여러 기능을 블록으로 나누어서 구현해도 문제가 없다.
- 관련 기능 별로 하나의 익스텐션 블록에 묶는게 좋아보인다.
extension Int {
func multiply(by n: Int) -> Int {
return (self * n)
}
}
print(3.multiply(by: 2)) // 6
print(4.multiply(by: 5)) // 20
number = 3
print(number.multiply(by: 2)) // 6
print(number.multiply(by: 3)) // 9
생성자 추가
- 인스턴스를 초기화할 때 초기화에 필요한 데이터를 받을 수 있도록 여러 종류의 생성자를 만들 수 있음
- 타입 정의부에는 추가하지 않았더라도 익스텐션을 통해 추가가가능함
- 익스텐션으로 클래스 타입에 편의 이니셜라이저는 추가가능하나
- 지정 이니셜라이저는 추가할 수 없다.
- 지정 이니셜라이저와 디이니셜라이저는 클래스 타입의 구현부에 위치해야 함
- 이건 참조 타입이라 그런 것. 참조 타입은 상관없음
- 그럼 이걸 하기 위해서는 확장하고 싶은 타입의 프로퍼티에 대해서 이해하고 있어야 하겠군
extension String {
init(int: Int) {
self = "\(int)"
}
init(double: Double) {
self = "\(double)"
}
}
let stringFromInt: String = String(int: 100)
// "100"
let stringFromDouble: String = String(double: 100.0)
// "100.0"