우리는 심심치 않게 Extension을 활용한다. 필자는 문자열이 주어졌을 때, 해당 문자열의 각 요소에 접근하기 위해 사용한 경우가 있다.
import Foundation
extension String {
subscript(_ index: Int) -> Character {
return self[self.index(self.startIndex, offsetBy: index)]
}
}
var name = "silverCastle"
print(name[0])
결과
s
그렇다면 "이 Extension은 도대체 무엇이고, 어떻게 사용할 수 있을까?"에 대해 알아보자.
익스텐션을 이용해 클래스, 구조체, 열거형 혹은 프로토콜 타입에 기능을 추가할 수 있다.
원본 코드를 몰라도 그 타입에 대한 기능을 확장할 수 있다.
라고 공식 문서에서 설명하고 있다.
또한, 이 Extension으로 다음과 같은 기능을 사용할 수 있다고 한다.
한마디로 Extension은 확장한다. 무엇을? 특정 타입을!
Extension은 extension
키워드를 사용하여 선언한다.
let beforeNum = 5
print(beforeNum) // 5
print(beforeNum.ten) // 10
extension Int {
var ten: Int { return 10 }
}
let afterNum = 5
print(afterNum) // 5
print(afterNum.ten) // 10
위 예시는 특정 타입 즉, Int 타입을 확장한 것인데 예시와 같이 연산 프로퍼티
를 사용할 수 있다. 연산 프로퍼티니까 ten은 상수 let이 아닌 변수 var 키워드를 사용해야겠다. 잘 모르겠다면?
또한, Extension을 정의하여 특정 타입에 새 기능을 추가하면 Extension을 사용하기 전인 beforeNum도 해당 기능을 사용할 수 있다. 즉, Extension의 위치는 상관없다.
연산 프로퍼티는 가능하지만 저장 프로퍼티와 프로퍼티 감시자는 사용할 수 없음을 알아두자.
extension Int {
var ten = 10 // Extensions must not contain stored properties
}
Extension은 designated initializers
와 deinitializers
를 추가할 수 없다. 단, deinitializers
는 추가할 수 있다.
class Person {
var name: String = ""
var age: Int = 0
}
extension Person {
deinit { // Deinitializers may only be declared within a class or actor
print("deinit")
}
init { // Designated initializer cannot be declared in an extension of 'Person'; did you mean this to be a convenience initializer?
print("init")
}
convenience init(_: String) { // 가능
print("convenience init")
}
}
사용자가 생성한 구조체에 대해 새로운 생성자를 정의하지 않았다면? Memberwise Initializer + 새로운 생성자
struct Person {
var name: String
var age: Int
}
extension Person {
init(exName: String, exAge: Int) {
self.name = exName
self.age = exAge
}
}
let person1 = Person(exName: "silverCastle", exAge: 26)
let person2 = Person(name: "silverCastle", age: 26)
구조체에 대해 생성자를 생성하지 않았으므로 기본으로 제공해주는 Memberwise Initializer + extension을 통해 생성한 생성자 둘 다 사용할 수 있다.
사용자가 생성한 구조체에 대해 새로운 생성자를 정의했다면? 새로운 생성자
struct Person {
var name: String
var age: Int
init(exName: String, exAge: Int) {
self.name = exName
self.age = exAge
}
}
let person1 = Person(exName: "silverCastle", exAge: 26)
let person2 = Person(name: "silverCastle", age: 26) // Incorrect argument labels in call (have 'name:age:', expected 'exName:exAge:')
이번에는 구조체에 생성자를 정의하였으므로 person2와 같이 Memberwise Initializer를 사용할 수 없다.
Memberwise Initializer는 모든 프로퍼티를 초기화할 수 있게 해주고 자동으로 제공되는 생성자임을 알아두자.
Extension을 이용해 존재하는 타입에 Instance Method
와 Type Method
를 추가할 수 있다.
extension Int {
func printNum() {
print("num: \(self)")
}
}
100.printNum() // num: 100
위의 예시는 인스턴스 메서드를 추가한 예이다.
extension Int {
mutating func multiple() {
self = self * 2
}
}
var num = 10
num.multiple()
print(num) // 20
위의 예시처럼 Mutating 키워드를 사용함으로써 자기 자신의 값을 바꿀 수 있다.
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
}
}
}
var num: Int = 10
print(num.kind) // positive
공식 문서에 있는 위 예제는 Int에 중첩형 enum을 추가한 예제이다. Kind라고 불리는 열거형은 Int를 음수, 0, 양수로 표현한다고 한다.