일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- NumberFormatter
- Mock
- mvvm
- xcode
- 부트캠프
- IOS
- Navigation
- 독후감
- 아이폰
- 야곰아카데미
- View Life Cycle
- 독서후기
- roundingMode
- Structures and Classes
- viewcontroller
- contentInset
- Failed to register bundle identifier
- 책후기
- Codegen
- Modality
- @available
- UIResponder
- human interface guidelines
- SWIFT
- 스타트업주니어로살아남기
- SWIFTUI
- NotificationCenter
- 스위프트
- delegation
- Info.plist
- Today
- Total
호댕의 iOS 개발
[Swift 기본 문법] Extension 공식문서 본문
Swift Programming LauguageGuide를 보고 작성한 글입니다.
📖 Extension은 뭘까?
Extension은 이미 존재하는 클래스, 구조체, 열거형, 프로토콜 타입에 새로운 기능을 추가하기 위해 존재한다.
이는 원래 코드에 접근할 수 없는 type을 확장하는 기능을 한다.
이는 Objective-C에서 categories와 유사하다. (단 Extension의 경우 따로 이름이 없다)
Extension의 기능
- 연산 인스턴스 프로퍼티나 연산 타입 프로퍼티를 추가한다
- 인스턴스 메서드나 타입 메서드를 정의한다
- 새로운 이니셜라이저를 제공한다
- subscript를 정의한다
- 새로운 중첩 타입을 정의하거나 사용한다
- 이미 존재하는 타입에 프로토콜을 채택한다
Swift에선 프로토콜에 Extension을 활용하여 프로토콜에 대한 기본 구현을 할 수도 있다.
✏️ Extension 작성은 어떻게 할까?
타입을 선언할 때처럼 대문자 카멜케이스를 사용해 이름을 작성하고 앞에 extension 키워드를 붙여주면 된다.
extension TypeName {
// 추가할 기능에 대해 작성해주면 됨
}
또한 extension에 하나 이상의 프로토콜을 채택할 수 있다. 이는 클래스나 구조체에서 하던 것처럼 그대로 해주면 된다.
프로토콜 필수 요구사항은 extension 내부에 구현해주면 된다.
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
🧮 Computed Properties
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"
이렇게 기능을 추가하여 computed property가 Double 값이 특정 길이 단위로 표현되어야 함을 나타낸다.
이는 연산 프로퍼티를 사용할 때처럼 dot syntex를 통해 사용하면 된다.
⛔️ 여기서 주의할 점!!
Extension은 새로운 computed property는 추가할 수 있지만 stored perperty는 추가할 수 없다. 또한 기존 프로퍼티의 property observer도 추가할 수 없다.
(이는 기존 타입 내에서만 추가 가능)
🏁 Initializer
Extension에선 기존에 존재하는 타입에 새로운 이니셜라이저를 추가할 수 있다.
이를 통해 타입을 원래 구현할 때 포함되지 않았던 초기화 옵션을 추가로 제공할 수 있고, 다른 타입을 확장하여 사용자 정의 타입을 초기화의 매개변수로 사용할 수도 있다.
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()
}
이런 3개의 사용자 정의 타입이 있고 Rect 타입에 Point와 Size의 인스턴스를 생성했다고 가정해보자.
그럼 Extension을 통해 사용자 정의 타입을 매개변수로 초기화를 해줄 수 있다.
물론 Rect은 구조체이기 때문에 기본 이니셜라이저나 멤버와이즈 이니셜라이저를 사용할 수 있다.
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
하지만 이런 식으로 Extension에 이니셜라이저를 정의해줄 수도 있다.
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을 계산할 수 있는 것이다.
🔧 Method
Extension에서 새로운 인스턴스 메서드나 타입 메서드를 추가할 수 있다.
이렇게 추가해준 메서드는 나중에 기존 메서드를 사용하는 것처럼 그대로 사용할 수 있다.
또한 Extension을 통해 추가된 인스턴스 메서드의 경우 인스턴스 자체를 수정 및 변경할 수도 있다. 즉 구조체나 열거형에서 self나 프로퍼티를 수정하는 메서드의 경우 반드시 mutating 키워드를 붙여야 하는데 Extension에도 이를 구현할 수 있는 것이다.
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt is now 9
🧩 Subcripts
Extension에 새로운 subcript도 정의할 수 있다.
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에 subscript 문법으로 자리수를 찾을 수 있는 것을 구현할 수도 있는 것이다.
🪆 Nested Types
기존 클래스, 구조체, 열거형에 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
}
}
}
예시처럼 Int 타입에 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 + "
이렇게 연산 프로퍼티인 kind에 Kind 타입을 반환하도록 하여 사용할 수도 있다.
그렇다면 Extension에서 함수를 override할 수 있을까??
공식 문서에선 일단 이는 안된다고 말하고 있다.
class SuperClass { }
extension SuperClass {
@objc func otherNumber() -> Int {
return 1
}
}
class Subclass: SuperClass { }
extension Subclass {
override func otherNumber() -> Int {
return 2
}
}
var superClass = SuperClass()
var subClass = Subclass()
print(superClass.otherNumber()) // 1
print(subClass.otherNumber()) // 2
하지만 override func 앞에 @objc 키워드를 붙이면 정상적으로 override가 가능하긴 하다.
Objective-C와의 호환성을 위해 Extension에서 override가 가능한 것이다.
하지만 SuperClass에서도 @objc 함수를 Extension에 작성해야 한다.
위처럼 Objective-C에선 저렇게 Extension에서 override를 하는 것이 가능하나, Swift에선 원칙적으로 extension에서 override를 하는 것을 금지하고 있다.
'Software Engineering > Swift' 카테고리의 다른 글
[Swift 기본 문법] Collection(Array / Dictionary / Set / KeyValuePairs) (0) | 2022.04.27 |
---|---|
[Swift 기본문법] Generic & Result 공식문서 보기 (0) | 2022.04.26 |
[Swift] 안전하게 index 접근하기 (0) | 2022.03.11 |
[SwiftUI] Property Wrapper (@State, @Binding, @StateObject, @ObsevedObject, @EnvironmentObject) (0) | 2022.02.26 |
[알고리즘] for 문을 거꾸로 돌 순 없을까? (프로그래머스 문제) (0) | 2022.01.23 |