티스토리 뷰
Extensions — The Swift Programming Language (Swift 5.5)
Extensions Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you don’t have access to the original source code (known as retroactive modeling). Extensions
docs.swift.org
1차 수정 (2022 / 02 / 28) : 의역, 오타 및 스타일 수정
Extensions
익스텐션(Extensions) 은 기존의 클래스, 구조체, 열거형, 프로토콜 타입에 새로운 기능을 추가합니다. 원본 소스 코드에 접근할 수 없는 타입을 확장하는 기능이 포함됩니다. (retroactive modeling 으로 알려져있다) 익스텐션은 Objective-C의 카테고리(categories) 와 유사합니다. Objective-C의 카테고리와는 달리, Swift의 익스텐션은 이름을 가지지 않습니다.
Swift의 익스텐션은 다음과 같은 일을 할 수 있습니다.
- 연산 인스턴스 프로퍼티 (computed instance property) 와 연산 타입 프로퍼티(compute type property) 추가
- 인스턴스 메소드와 타입 메소드 정의
- 새로운 이니셜라이져 제공
- 첨자(subscripts) 제공
- 새로운 중첩 타입의 정의 및 사용
- 기존의 타입을 프로토콜에 맞춘다
Swift에서 프로토콜을 확장하여 요구사항의 구현을 제공할수 있습니다. 혹은 프로토콜은 준수하는 타입이 활용할 수 있는 추가적인 기능을 추가합니다.
익스텐션은 타입에 새로운 기능을 더한다. 하지만 기존의 기능을 오버라이딩(=재정의) 하지 못한다.
Extension Syntax (익스텐션 첨자)
extension 키워드를 사용해 익스텐션을 정의합니다.
extension SomeType {
// new functionality to add to SomeType goes here
}
익스텐션은 기존 타입을 확장하거나 하나이상의 프로토콜을 채택할 수 있습니다. 프로토콜 호환성을 추가하기 위해 구조체 및 클래스 이름 뒤에 프로토콜의 이름을 적어줍니다.
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
익스텐션은 기존 제네릭(generics) 타입을 확장하기위해 사용할 수 있습니다. 제네릭 타입에 조건적으로 기능을 추가할수도 있습니다.
기존 타입에 새로운 기능을 추가하기 위해 익스텐션을 정의하면, 새로운 기능은 해당 타입의 기존 모든 인스턴스에서 사용할 수 있습니다. 익스텐션이 정의되기 전에 생성되었어도 가능합니다.
Computed Properties (연산 프로퍼티)
익스텐션은 기존의 타입에 연산 인스턴스 프로퍼티 (computed instance property) 와 연산 타입 프로퍼티(compute type property) 를 추가할 수있습니다.
아래 예제는 Double 타입에 5개의 연산 인스턴스 프로퍼티를 추가합니다.
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"
이 연산 프로퍼티는 Double의 값이 특정 단위 길이로 고려되야함을 표현합니다. 연산 프로퍼티 실행되었지만, 이 프로퍼티의 이름은 부동 소수점 값에 . 을 사용해 사용할 수 있습니다.
예제에서 1.0의 Double 값은 1 미터로 표현됩니다. 이것이 m 연산 프로퍼티가 self를 반환하는 이유 입니다. (표현식 1.m은 Double 값 1.0 을 계산하는 것으로 간주됩니다)
다른 단위들은 미터로 측정한 값을 표현하기 위해 변환을 요구합니다.
1km 는 1000m 와 같습니다. 따라서 1_000.00 을 곱해 미터값을 리턴합니다.
마찬가지로 3.28084 ft 는 1m 와 같습니다. 따라서 3.28084을 나눠 미터값을 리턴합니다.
이러한 프로퍼티는 읽기-전용 연산 프로퍼티이기 때문에 get 키워드 없이 짧게 표현됩니다. 리턴 값은 Double 타입이며 Double 이 허용되는 수학적 계산에 모두 사용될 수 있습니다.
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// Prints "A marathon is 42195.0 meters long"
익스텐션은 새로운 연산 프로퍼티를 추가할 수 있습니다. 하지만 기존의 프로퍼티에 저장 프로퍼티나 저장 프로퍼티 옵져버를 추가할 수는 없습니다.
Initializers (이니셜라이져)
익스텐션은 기존의 타입에 새로운 이니셜라이져를 추가할 수 있습니다. 이는 다른 타입을 확장하여 사용자 지정(custom) 타입을 이니셜라이져 파라미터로 받아들이는 것을 가능하게 합니다. 또는 타입의 원본 구현의 일부로 포함되지 않는 추가적인 이니셜라이져 옵션을 제공하는 것을 가능하게 합니다.
익스텐션은 클래스에 새로운 편의 이니셜라이져(convenience initializers) 추가할 수 있습니다. 하지만 새로운 지정 이니셜라이져(designated initializers) 혹은 디이니셜라이져(deinitializers) 를 추가할 수는 없습니다. 지정 이니셜라이져와 디이니셜라이져는 반드시 원본 클래스 구현에서 제공되어야 합니다.
익스텐션을 사용하여 `저장된 모든 타입에 기본값을 제공하고, 사용자 정의 이니셜라이져를 정의하지 않은` 값 타입에 새로운 이니셜라이져를 추가한다면, 해당 값 타입을 위해 익스텐션 이니셜라이져 내에서 기본 이니셜라이져와 멤버와이즈 이니셜라이져를 호출할 수 있습니다. 값 타입의 원본 구현의 일부로 이니셜라이져를 작성한 경우에는 호출할 수 없습니다.
다른 모듈에 선언된 구조체에 이니셜라이져를 추가하기 위해 익스텐션을 사용한다면, 새로운 이니셜라이져는 정의 모듈에서 이니셜라이져를 호출할 때까지 self 에 접근할 수 없습니다.
아래 예제는 사용자 정의 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()
}
Rect 구조체는 모든 프로퍼티에 기본 값을 제공하기 때문에 자동적으로 기본 이니셜라이져와 멤버와이즈 이니셜라이져를 가집니다. 이 이니셜라이져는 새로운 Rect 인스턴스를 생성하기 위해 사용됩니다.
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
특정 중앙점(center point)과 크기(size)를 갖는 이니셜라이져를 추가하기 위해 Rect 구조체를 확장(extend)할 수 있습니다.
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 값을 기반으로, 원점 계산을 하는 것부터 시작합니다. 이니셜라이져는 자동 멤버와이즈 이니셜라이져 init(origin:size:) 를 호출합니다. 이를 통해 새로운 원점과 크기 값을 계산합니다.
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 (메소드)
익스텐션은 기존의 타입에 새 인스턴스 메소드와 타입 메소드를 추가할 수 있습니다.
아래 예제는 repetitions 이라는 새로운 인스턴스 메소드를 추가합니다.
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
repetitions(task:) 메소드는 () -> Void 타입의 단일 인수를 가집니다. 이 타입은 파라미터가 없고 값을 리턴하지 않는 함수를 가르킵니다.
익스텐션의 정의 후, 모든 정수(Int) 타입에 repetitions(task:) 메소드를 사용할 수 있습니다.
3.repetitions {
print("Hello!")
}
// Hello!
// Hello!
// Hello!
Mutating Instance Methods (인스턴스 메소드 변경)
익스텐션으로 추가된 인스턴스 메소드는 인스턴스 자체를 수정 (혹은 변경) 할 수 있습니다. self 혹은 자신의 프로퍼티를 수정하는 구조체와 열거형 메소드는 반드시 mutating 을 작성해주어야 합니다. 원본 구현에서 메소드를 변형하는 것처럼 말입니다.
아래 예제는 Swift의 Int 타입에 새로운 변경(mutating) 메소드 square을 추가합니다.
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt is now 9
Subscripts (첨자)
익스텐션은 기존의 타입에 새 첨자를 추가할 수 있습니다. 아래 예제는 Swift의 Int 타입에 정수 첨자를 추가합니다. 이 첨자 [n] 은 오른쪽으로 부터 n 번째 숫자를 리턴합니다. (10진수)
- 123456789[0] returns 9
- 123456789[1] returns 8
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
만약 Int 값이 요청된 인덱스에 맞는 충분한 숫자를 가지고 있지 않으면, 첨자 구문(서브스크립트) 은 0을 리턴합니다. 숫자 0을 왼쪽을 채우듯이
746381295[9]
// [9] 는 오른쪽에서 10번째인데 값이 없으므로 0을 리턴한다
0746381295[9]
Nested Types(중첩 타입)
익스텐션은 기존의 클래스, 구조체, 열거형에 새로운 중첩타입을 추가할 수 있습니다.
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 에 새로운 중첩 열거형 Kind 를 추가합니다. 이 열거형은 특정 정수가 나타내는 종류를 표현합니다. (0, 음수 , 양수)
예제에서 새로운 연산 인스턴스 프로퍼티 kind 도 추가합니다. Kind 열거형에 맞는 정수 값을 리턴합니다.
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 + "
위 함수 printIntegerKinds는 Int 배열을 입력으로 받고, 값들을 반복(iterates) 합니다. 배열의 각 정수에서, 함수는 kind 연산 프로퍼티를 사용해 적절한 설명을 출력합니다.
number.kind 는 이미 Int.Kind 타입으로 인식되어 있습니다. 이 때문에 모든 Int.Kind 케이스 값은 switch 문에서 축약형으로 작성될 수 있습니다. (Int.Kind.negative -> .negative 이런 식으로)
구조체, 클래스, 열거형이 너무 길어지면 가독성이 떨어진다.
이를 해결하고자 Extenstion을 사용해 코드를 분리한다.
Swift의 기존 타입과 프로토콜에 새로운 기능을 추가할 때 사용한다.
또 이 문서에는 안 나왔지만 View를 작성할 때도 작은 단위로 쪼개고자 사용한다.
Extension 끗
😄
'Swift > Swift 공식문서' 카테고리의 다른 글
[Swift] 공식문서 23 - Gernerics (제네릭) (0) | 2021.11.05 |
---|---|
[Swift] 공식문서 22 - Protocols (프로토콜) (0) | 2021.11.04 |
[Swift] 공식문서 20 - Nested Types (중첩 타입) (0) | 2021.11.01 |
[Swift] 공식문서 19 - Type Castng (타입 캐스팅) (0) | 2021.10.31 |
[Swift] 공식문서 18 - Concurrency(동시성) (0) | 2021.10.30 |
- Total
- Today
- Yesterday
- swiftUI 기초
- Swift
- 부스트캠프
- 코딩테스트
- 책리뷰
- 코딩 테스트
- TODO
- Swift 서버
- Swift문법
- 날씨어플
- 애플
- SwiftUI
- Swift DocC
- todo앱
- 필독서
- ios
- 디자인 패턴
- 프로그래머스
- UX
- 코딩
- vapor
- 책
- 책후기
- Swift 디자인 패턴
- Swift공식문서
- 부스트캠프iOS
- 부스트캠프7기
- 개발
- Combine
- 앱개발
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |