확장(Extensions)
- 기존 클래스, 구조체, enum, protocol새로운 기능을 추가하는 것을 말한다. 기존 소스코드에서 접근할 수 없는 타입을 확장하는 기능도 추가된다.(retroactive modeling)
- Objective-C의 categories와 유사하다. Swift에서는 name을 가지지 않는다.
Swift에서 확장이 하는 일은
- computed 프로퍼티와 computed 타입 프로퍼티를 추가
- 인스턴스 메소드와 타입 메소드를 정의
- 새로운 초기화 제공
- subscripts 정의
- 새로운 nested 타입을 정의하고 사용
- 프로토콜에 대해서 기존 타입 conform 만들기
주의 : 새로운 기능을 타입에 추가하지만 기존 기능을 오버라이드할 수는 없다.
확장 문법
- extension 키워드를 사용한다.
extension SomeType {
}
- 기존 타입이 1개 이상의 프로토콜을 채택할 수 있게 확장할 수 있다. 프로토콜 이름은 클래스나 구조체에서와 같은 방식으로 작성한다.
extension SomeType: SomeProtocol, AnotherProtocol {
//여기에 프로토콜을 만죽시키는 구현 작성
}
주의: 새로운 기능을 기존 타입에 추가하기 위해서 extension을 정의하는 경우라면 새로운 기능은 해당 타입의 기존 모든 인스턴에서 가능하다. 심지어 extension이 정의되기 전에 생성된 것도 해당된다.
Computed 프로퍼티
- extension은 computed 인스턴스 프로퍼티와 computed 타입 프로퍼티를 기존 타입에 추가할 수 있다. 이 예제에서 5개 computed 인스턴스 프로퍼티를 Swift의 빌트인 Double 타입에 추가하여, 거리 단위와 관련도니 일을 처리하도록 지원한다.
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self/100.0 }
var mm: Double { return self/1_000.0 }
var ft: Double { return self/3.28084 }
}
let oneInch = 25.4mm
print("One inch is \(oneInch) meters")
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
- 3개 computed 프로퍼티는 Double 값이 길이를 나타내는 단위로 사용했다는 것을 보여준다. 비록 computed 프로퍼티로 구현했지만 이 프로퍼티의 이름은
.문법으로 소수점 리터럴로 추가할 수 있다. 이 리터럴은 거리를 변환하는데 사용한다. - 이 예제에서 1.0인 Double 값은 1 미터를 나타내는데 사용한다. 이는 m computed 프로퍼티가 self를 반환하는 이유이기도 하다. 1.m이라는 ㅛ현은 1.0의 Double 값을 계산하는 것을 의미한다.
- 다른 단위는 미터 단위로 변환하기 위해서 변환이 필요하다. 1 킬로미터는 1,000미터와 같다. 따라서 km computed 프로퍼티는 변환을 위해서 1_000.00 값을 곱한다. 1미터에 3.28084 ft이므로 ft computed 프로퍼티는 3.28084로 Double 값을 나눠서 ft에서 미터로 변환이 가능하다.
- 이 프로퍼티들은 읽기전용 computed 프로퍼티이므로 get 키워드 없이도 표현할 수 있다. 반환값은 Double 타입이고 Double이 가능한 어떤 수학적 계산에 사용할 수 있다.
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
- 주의: Extension은 새로운 computed 프로퍼티를 추가할 수 있지만, stored 프로퍼티나 프로퍼티 오브져버를 기존 프로퍼티에 추가할 수는 없다.
초기화
- 새로운 초기화를 기존 타입에 추가할 수도 있다. 이렇게 하면 초기화 파라미터로 여러분이 직접 작성한 타입을 받을 수 있게 타입을 확장할 수 있다. 그리고 원본 타입의 구현의 일부로 추가히지 않고도 추가적으로 초기화 옵션을 제공한다.
- 클래스에 새로 편하게 초기화를 추가하지만, 새로 지정한 초기화나 deinitializer를 추가할 수는 없다. 지정한 초기화와 deinitializer는 반드시 원본 클래스에 구현해야만 한다.
- 주의 값타입에 초기화를 추가하기 위해 extension을 사용한다면, 확장 초기화에서 기본 초기화나 멤버 초기화를 호출할 수 있다.
- 아래 예제에서 정의한 Rect 구조체로 기하학적인 사각형을 나타낸다. Size와 Point라는 2개 구조체를 지원한다. 이것들은 속성으로 0.0ㅇㄹ 기본값으로 제공한다.
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
}
- Rect 구조체는 모든 프로퍼티에 대해서 기본 값을 제공하므로, 자동으로 기본 초기화와 프로퍼티와 관련된 초기화가 적용된다. 이 초기화로 새로운 Rect 인스턴스를 생성할 수 있다.
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
Rect 구조체를 확장하여 특정 center point와 size를 가지는 초기화를 추가할 수 있다.
extension Rect { init(center: Point, size: Size) { let originX = center.x - (size.width / 2) let originY = center.y - (size.height / 2) self.init(origin: Point(x: originX, y: originY), size: size) } }새로운 초기화는 center와 size 값이 제공하는 값을 바탕으로 origin 포인트를 적절하게 계산한다. 다음으로 초기화는 구조체의 자동으로 멤버 프로퍼티를 초기화하는 init를 호출한다. 이것으로 새로운 origin과 size 값을 적절하게 각 프로퍼티에 저장하게 된다.
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
- 주의: 여러분이 직접 extension으로 새로운 초기화를 제공하면, 초기화가 마치고나서 각 인스턴스가 완전히 초기화되었는지 여부에 대한 책임을 여러분이 져야한다.
메소드
- extention으로 새로운 인스턴스 메소드와 타입 메소드를 기존 타입에 추가할 수 있다. 다음 에제에서 repetitions라는 새로운 인스턴스 메소드를 추가할 것이다.
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
- repetitions 메소드는 타입이 ()->Void인 단일 인자를 가지고 이것은 파라미터가 없고 반환값이 없는 함수를 말한다.
- extention을 정의한 후에, 정수 횟수만큼 task를 수행하는 정수값에 repetitions 메소드를 호출할 수 있다.
3.repetitions({
print("Hello!")
})
- 클로저문법을 통해 더욱 간결한 호출이 된다.
3.repetitions{
print("Goodbye!")
}
뮤테이팅 인스턴스 메소드
- extension을 가지는 인스턴스 메소드는 인스턴스 자체를 변경할 수 있다. 구조체와 enum 메소드는 self를 변경하거나 해당 프로퍼티들은 반드시 mutating 키워드로 인스턴스 메소드에 표시를 해야한다. 원본 구현에서 변경한 메소드라는 것을 뜻한다.
- 아래 예제에는 새로운 뮤테이팅 메소드 square를 Int 타입에 추가한다. 원래 값을 제곱한다.
extension Int {
mutating func sqaure() {
self = self * self
}
}
var someInt = 3
someInt.square()
Subscript
- 기존 타입에 extension은 새로운 subscript을 추가할 수 있다. 아래 예제에서는 정수 subscript를 Swift의 빌트인 Int 타입에 추가한다. 여기서 subscript [n]은 숫자의 오른쪽을 기준으로 n번째 위치한 수를 반환한다.
123456789[0]는 9를 반환한다.
123456789[1]는 8일 반환한다.
extension Int {
subscript(var digitIndex: Int) -> Int {
var decimalBase = 1
while digitIndex > 0 {
decimalBase *= 10
--digitIndex
}
return (self / decimalBase) % 10
}
}
746381295[0]
746381295[1]
746381295[2]
746381295[8]
- 요청한 인덱스만큼 자릿수를 지원하지 않는 경우 subscript는 0을 반환한다. 마치 왼쪽으로 0을 추가한 것과 같은 동작이다.
746381295[9]
0746381295[9]
Nested 타입
- 새로운 nested 타입을 기존 클래스, 구조체, enum에 추가할 수 있다.
extension Int { enum Kind { case Negative, Zero, Positive } var kind: Kind { switch self { case 0: return .Zero case let x where x > 0 : return .Positive default: return .Negative } } }- Int 타입에 새로운 nested enum을 추가했다. Kind라는 enum으로 정수가 어떤 종류인지를 표현하는데 음수, 0, 양수인지를 나타낸다.
- 다음 예제에서는 Int 타입에 새로운 computed 인스턴스 프로퍼티 kind를 추가한다. 이것은 정수에 대해서 Kind enum의 case를 반환한다.
func printIntegerKinds(numbers: [Int]){
for number in numbers {
switch number.kind {
case .Negative:
print("- ", terminator: "")
case .Zero:
print("0 ", terminator: "")
case .Positive:
print("+ ", terminator: "")
}
}
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
- 함수 printIntegerKinds는 Int 값의 배열을 입력으로 받아서 차례로 사용한다. 배열에서 각 정수에 대해서 이 함수는 kind computed 프로퍼티로 여기고 적절한 설명을 출력한다.
- 주의: number.kind는 이미 Int.Kind 타입으로 쓰이고 있다. 이런 이유 때문에, Int.Kind의 모든 case 값에 대해서 switch 내부에서 단축형으로 쓴다.(Int.Kind.Negative 대신에 .Negative를 사용)
정의
사용용도
문법
예제
Quiz
- 정의에 대한 질문
- 용도에 대한 질문
- 예제 관련