Search

[Swift] Extensions

The Swift Programming Language Documents를 참고해서 정리했습니다. 여러 가지 생각들이 버무러져 있습니다.
contents
*이전 포스팅은 [Swift] Strings and Characters 에서 보실 수 있습니다.

Extensions

Extension은 기존에 존재하는 타입에 새로운 기능을 추가할 때 사용할 수 있습니다.
클래스 뿐만 아니라 구조체, 열거형, Protocol 타입에 새 기능을 추가할 때 Extension을 사용하면 됩니다.
Extension은 extension 키워드와 함께 선언됩니다.
extension SomeType { // ... }
Swift
복사
그러면, Extension를 사용해서 어떤 기능들을 추가할 수 있을까요?
⓵ 기존 타입이 Protocol을 준수하게끔 만들 수 있습니다.
⓶ Computed Instance, Type 속성을 추가할 수 있습니다.
⓷ Instance, Type Method를 정의합니다.
⓸ 새로운 initializer를 제공합니다.
⓹ Subscript를 정의합니다.
⓺ 새로운 중첩 타입을 정의하고 사용합니다.
Extension을 통해서 추가된 새로운 기능들은 해당 타입의 모든 기존 인스턴스에서 사용 가능합니다. 해당 인스턴스가 Extension으로 정의하기 전에 생성된 경우라도 동일하게 적용됩니다.
Extension에서 새로운 기능을 추가하는건 가능하지만, 기존에 있는 기능을 재정의하는 건 불가능합니다.

Conform protocol

⓵ 기존 타입이 Protocol을 준수하게끔 만들 수 있습니다.
Extension은 기존에 존재하는 타입이 하나 이상의 프로토콜을 채택하도록 만들 수 있습니다.
Protocol Conformance를 추가하려면 클래스, 구조체에 하는 것과 동일하게 Protocol 이름을 작성해주시면 됩니다.
extension SomeType: SomeProtocol, AnotherProtocol { // ... }
Swift
복사
Extension 시에 존재하는 Generic Type도 확장 가능합니다. Where절을 사용해서 Generic Type에 조건부로 기능을 추가할 수 있게끔 만들 수도 있습니다.
extension SomeType where Item: Equtable { // ... }
Swift
복사

Computed Instance

⓶ Computed Instance, Type 속성을 추가할 수 있습니다.
Extension은 Computed 인스턴스를 기존에 존재하는 타입에 추가할 수 있습니다.
예를 들어서, 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 } }
Swift
복사
Computed 프로퍼티를 사용해서 계산된 Double 타입의 값을 반환합니다. 해당 프로퍼티는 read-only Computed 프로퍼티이기 때문에 get 키워드 없이 코드를 작성할 수 있습니다.
Double 타입 인스턴스라면 해당 Computed 프로퍼티를 사용할 수 있습니다.
let oneInch = 25.4.mm let threeFeet = 3.ft
Swift
복사
Extension에 새로운 Computed 프로퍼티를 추가할 수 있지만, Stored 프로퍼티를 추가하거나 기존 속성에 Property Observer를 추가하는 것은 불가능합니다.

Methods

⓷ Instance, Type Method를 정의합니다.
Extension을 사용해서 Instance, Type Method를 기존 타입에 추가할 수 있습니다.
예를 들어서, repetitions 라는 인스턴스 메서드를 Int 타입에 추가하면 Int 타입 인스턴스에서 해당 메서드를 호출해서 원하는 작업을 수행할 수 있게 됩니다.
extension Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } } 1.repetitions { print("Hello!") } // Hello!
Swift
복사
Extension을 사용하여 추가된 메서드는 인스턴스 자체를 수정하거나 내부 프로퍼티를 수정할 수도 있습니다. 구조체나 열거형에서 인스턴스나 내부 프로퍼티를 수정하는 경우 mutating 키워드로 표시를 해주어야 합니다.
extension Int { mutating func square() { self = self * self } }
Swift
복사

Initializer

⓸ 새로운 initializer를 제공합니다.
Extension을 사용해서 기존 타입에 새로운 initializer를 추가할 수 있습니다.
타입의 기존 구현에 포함되지 않는 추가 초기화 옵션을 제공할 수 있게 됩니다. 물론, 새로 추가되는 initializer는 Convience initializer입니다. designated initializerdeinitializer는 Extension에서 추가할 수 없습니다. 항상 기존 구현에서 제공해주어야 합니다.
커스텀 initializer를 정의하지 않은 Value Type에 initializer를 추가하는 경우, Extension의 initializer 내에서 해당 타입에 대한 default, memberwise initializer를 호출할 수 있습니다. 하지만, initializer를 Value Type 기존 구현의 일부로 작성한 경우엔 그렇지 않습니다.
Extension을 사용하여 다른 module에 선언된 구조체에 initializer를 추가하는 경우, 새로운 initializer는 정의된 모듈에서 initializer를 호출할 때까지 self에 액세스할 수 없습니다.
예를 들어서, Rect, Size, Point 구조체를 정의했다고 해봅시다. Rect, Size, Point 구조체는 모든 속성에 대해 기본값을 제공해줍니다.
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() }
Swift
복사
기본 initializer를 가지고 새로운 Rect 인스턴스를 생성할 수 있습니다.
let defaultRect = Rect() let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
Swift
복사
Rect의 Extension에서 특정 center point, size를 사용하는 추가 initializer를 제공했습니다.
새로 제공된 initializer는 Center, Size를 적절한 Origin으로 계산해서 구조체의 memberwise initializer를 호출한 다음 적절한 속성에 해당 값을 저장합니다.
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) } }
Swift
복사
Extension을 통해서 새로운 initializer를 제공하는 경우에도 init이 완료된 후에 각 인스턴스가 완전히 초기화되었는지 확인해야 합니다.
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
Swift
복사

Subscripts

⓹ Subscript를 정의합니다.
Extension을 사용해서 기존 타입에 새로운 Subscript를 추가할 수 있습니다.
예를 들어서, Int 타입에 subscript를 추가한다고 해봅시다. subscript를 통해서 index로 들어오는 숫자에 위치하는 수를 반환해줄겁니다.
extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0..<digitIndex { decimalBase *= 10 } return (self / decimalBase) % 10 } }
Swift
복사
해당 자리에 해당하는 수가 반환됩니다.

Nested Types

⓺ 새로운 중첩 타입을 정의하고 사용합니다.
새로운 Nested Type을 기존 클래스, 구조체, 열거형에 추가할 수 있습니다.
예를 들어서, 새로운 열거형을 Int 타입에 추가한다고 해봅시다. Kind라는 특정 정수가 나타내는 숫자의 종류를 케이스별로 나눈 열거형이 있습니다.
extension Int { enum Kind { case negative, zero, positive } }
Swift
복사
그리고 해당 열거형 타입을 반환하는 kind라는 Computed 프로퍼티를 만들었습니다.
extension Int { var kind: Kind { switch self { case 0: return .zero case let x where x > 0: return .positive default: return .negative } } }
Swift
복사
kind를 사용해서 switch문안에서 Int가 아닌 .negative와 같은 열거형 케이스로 작성할 수 있게 되었습니다. 훨씬 코드를 보기가 쉬워졌네요.
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("") }
Swift
복사
출력이 잘 되는걸 볼 수 있습니다.

참고 자료