출처 : https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
Extension은 이미 존재하는 class/struct/enum/protocol에 새로운 기능을 추가합니다
이는 원본 소스코드에 접근권한이 없어도 확장할 수 있습니다
(이렇게 원본을 수정하지 않고 재사용하여 새로운 컨셉을 표현하는 것을 retroactive modeling이라 합니다)
Extension은 Objective-C에서의 category와 비슷한 개념이나, extension은 이름을 갖지 않는다는 차이가 있습니다
Extension은 이런게 가능합니다
- 계산 프로퍼티 추가 (저장 프로퍼티/프로퍼티 옵저버는 안됨)
- 메서드 추가
- 생성자 추가
- subscript 추가
- Nested type 추가
- 프로토콜 생성/채택
✅NOTE
저장 프로퍼티는 왜 안될까?
저장 프로퍼티가 늘어난다는 것은 타입이 요구하는 메모리 사이즈가 변경됨을 뜻합니다
Extension은 일부 코드 영역에만 적용할 수 있으므로 하나의 타입에 대해 각 모듈마다 서로 다른 메모리 사이즈를 요구하게 됩니다
그래서, 컴파일러는 어떤 타입이 요구하는 메모리 사이즈를 정의할 때, 모든 모듈을 뒤지며 이 타입에 대한 extension이 있는지+이 모듈에선 얼만큼의 메모리가 필요한지 계산해야 합니다
애플의 공식적인 답변은 아니지만, 컴파일러에게 이 작업이 부담이기에 막았다고 합니다
(모듈간 인스턴스를 이동시킬 때도 골치아플듯?)
참고 레딧글
Swift에서는 심지어 프로토콜을 extension할 때는, 요구사항이 아닌 '구현체' 자체를 추가할 수도 있습니다
❗️주의❗️
Extension은 새로운 기능을 추가할뿐, 기존의 기능을 override하진 못합니다
extension SomeType {
// new functionality to add to SomeType goes here
}
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
✅NOTE
Extension은 런타임에 추가하는 개념이 아닙니다.
따라서, 어느 코드 위치에 선언하든 해당 타입의 모든 인스턴스에 영향을 미칩니다
계산 프로퍼티를 추가할 수 있습니다
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.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"
서론에서 언급했듯이, 저장프로퍼티와 프로퍼티옵저버는 extension으로 추가할 수 없습니다
새로운 생성자를 추가할 수도 있습니다
이를 통해, 우리가 정의한 custom 타입을 생성자의 파라미터로 받게 한다던지 원래 정의부에는 없던 추가적인 생성자 옵션을 줄 수 있습니다
extension으로 만든 생성자 역시 인스턴스를 fully initialize할 책임이 있다는걸 기억합시다 (= 모든 저장 프로퍼티 초기화)
class
는 designated 생성자와 소멸자를 추가할 수 없고 원본을 따라야 합니다
'convenience 생성자'만 추가할 수 있습니다
우선 값타입은 새로운 생성자를 class대비 자유롭게 추가할 수 있습니다
오히려 좀 더 우대(?)를 해주는 사항이 있는데
'default생성자' 혹은 'memberwise생성자'를 자동으로 받을 수 있는 케이스에서
- extension에 생성자를 추가한다고 해서 이 자동생성 조건이 깨지지 않습니다
(원래, default와 memberwise는 custom생성자가 하나라도 있으면 자동생성되지 않는다)
- extension에 추가할 새로운 생성자에서 이들을 호출할 수도 있습니다
다른 모듈에 정의된 struct를 생성자 extension하려면, self
에 접근할 수 없는 문제가 있다
original에 정의된 생성자를 호출하여, 저장 프로퍼티를 모두 초기화한 후에야 접근할 수 있다
(마치 class가 convenience 쓰듯)
메서드는 별다른 특이사항없이 추가가 가능합니다
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
3.repetitions {
print("Hello!")
}
// Hello!
// Hello!
// Hello!
mutating 인스턴스 메서드
값타입에 대해 self를 수정하는 메서드를 추가할 때는, original 구현과 똑같이 mutating 키워드를 붙혀주어야 합니다
subscript도 별다른 특이사항없이 추가가 가능합니다
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
Nested type 또한 특이사항없이 extension으로 추가할 수 있습니다
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
}
}
}
❗️주의❗️
Protocol의 Extension에서는 Nested Type을 확장할 수 없습니다