-
ExtensionsiOS/Swift 공식문서 2022. 2. 23. 17:03
Extensions
extension은 기존 클래스, 구조체, 열거형, 혹은 프로토콜에 새로운 기능을 추가해준다. 해당 타입들이 정의된 코드에 접근할 수 없는 경우에도 사용할 수 있다. 이를 retroactive modeling이라 한다. extension의 기능은 다음과 같다.
a. computed instance/type property를 추가할 수 있다.
b. instance/type method를 정의할 수 있다.
c. 새로운 initializer를 추가할 수 있다.
d. subscript를 정의할 수 있다.
e. 새로운 중첩 타입을 정의하여 사용할 수 있다.
f. 기존 타입이 프로토콜을 따르도록 할 수 있다.
Swift에서는 프로토콜도 확장하여 요구사항에 대한 구현을 제공하거나 추가 기능을 제공할 수 있다.
note
extension은 타입에 새로운 기능을 추가할 수는 있지만, 기존 기능을 오버라이딩할 수는 없다.
Extension Syntax
extension을 선언하려면
extension
키워드를 사용하면 된다.extension SomeType { // new functionality to add to SomeType goes here }
extension은 기존 타입이 하나 혹은 여러 개의 프로토콜을 따르도록 확장할 수 있다. 프로토콜 순응(conformance)을 추가하기 위해서, 콜론 뒤에 해당 프로토콜 이름을 적으면 된다.
extension SomeType: SomeProtocol, AnotherProtocol { // implementation of protocol requirements goes here }
extension을 사용하여 기존 generic 타입을 확장하거나, 혹은 조건부로 기능을 추가할 수도 있다. 이는 추후 포스팅을 참조하자.
note
extension을 정의하여 기존 타입의 기능을 확장했다면, 해당 타입의 모든 인스턴스에 대하여 새로운 기능을 사용할 수 있다. 이는 extension이 정의되기 전에 만들어진 인스턴스에도 적용된다.
Computed Properties
extension은 기존 타입에 computed인 instance/type property를 추가할 수 있다. 다음은 Double에 대하여 read-only computed property들을 추가로 정의한 예시이다.
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" let aMarathon = 42.km + 195.m print("A marathon is \(aMarathon) meters long") // Prints "A marathon is 42195.0 meters long"
note
extension은 기존 타입에 computed property는 추가할 수 있지만, stored property나 property observer는 추가할 수 없다.
Initializers
extension으로 새로운 initializer를 추가할 수 있다. 이를 통해 다른 타입을 확장하여 개발자가 만든 커스텀 타입을 initializer의 매개변수로 사용할 수 있게 하거나, 기존 구현에는 포함되지 않은 초기화 옵션을 추가적으로 제공할 수 있다.
extension으로 convenience initializer는 추가할 수 있지만, designated initializer나 deinitializer를 추가할 수는 없다. 이 둘은 반드시 초기 선언시에 제공되어야 한다.
모든 stored property에 대하여 기본값을 제공하고 custom initializer가 없는 value type의 경우, extension을 사용하여 initializer를 추가하면 extension의 initializer에서 default initializer와 memberwise initializer를 호출할 수 있다. 타입의 기존 구현에서 initializer를 정의한 경우에는 해당되지 않는다.
다른 모듈에 있는 구조체에 대하여 extension을 사용하여 initializer를 추가하는 경우, 새로운 initializer에서는 기존 모듈에서의 initializer를 호출하기 전까지는
self
에 접근할 수 없다.
다음 예시를 살펴보자. 다음과 같이 구조체들을 정의하자.
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
의 경우 모든 프로퍼티에 기본값이 있기 때문에, default initializer와 memberwise initializer를 자동으로 받는다.let defaultRect = Rect() let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
다음과 같이
Rect
구조체를 확장하여 새로운 initializer를 정의하자. 새로운 initializer에서도 기존의 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) } }
새로운 initializer를 사용하면 정상적으로 초기화된다.
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) // centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
Methods
extension을 통해 기존 타입에 인스턴스 메서드나 타입 메서드를 추가할 수 있다.
extension Int { func repetitions(task: () -> Void) { for _ in 0..<self { task() } } } 3.repetitions { print("Hello!") } // Hello! // Hello! // Hello!
Mutating Instance Methods
extension으로 추가된 메서드도 인스턴스 자체를 수정할 수 있다. 구조체나 열거형에서
self
혹은 프로퍼티를 수정하는 인스턴스 메서드의 경우 초기 구현하는 코드에서처럼 앞에mutating
을 적어야 한다.extension Int { mutating func square() { self = self * self } } var someInt = 3 someInt.square() // someInt is now 9
Subscripts
extension으로 기존 타입에 subscript를 추가할 수도 있다.
extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0..<digitIndex { decimalBase *= 10 } return (self / decimalBase) % 10 } } 746381295[0] // returns 5 746381295[1] // returns 9 746381295[2] // returns 2 746381295[8] // returns 7 746381295[9] // returns 0, as if you had requested:
Nested Types
기존 클래스, 구조체, 열거형에 중첩 타입을 추가할 수 있다. 다음은
Int
에Kind
열거형과kind
프로퍼티를 추가한 예시이다.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
값의 부호를 출력하는 코드이다.switch
문에서number.kind
의 타입이 추론되기 때문에 앞의Int.Kind
를 생략하고 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]) // Prints "+ + - 0 - 0 + "
Reference
https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
'iOS > Swift 공식문서' 카테고리의 다른 글
Generics (0) 2022.02.25 Protocols (0) 2022.02.24 Nested Types (0) 2022.02.23 Type Casting (0) 2022.02.23 Concurrency (0) 2022.02.23