[Swift] Extension을 알아보자

silverCastle·2023년 1월 21일
0
post-thumbnail

💡 Extension이란?

우리는 심심치 않게 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은 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
}

✍️ 생성자(Initializer)

🔍 Class일 경우

Extension은 designated initializersdeinitializers를 추가할 수 없다. 단, 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")
    }
}

🔍 Struct일 경우

사용자가 생성한 구조체에 대해 새로운 생성자를 정의하지 않았다면? 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는 모든 프로퍼티를 초기화할 수 있게 해주고 자동으로 제공되는 생성자임을 알아두자.

✍️ 메서드(Method)

Extension을 이용해 존재하는 타입에 Instance MethodType 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 키워드를 사용함으로써 자기 자신의 값을 바꿀 수 있다.

✍️ 중첩 타입 (Nested Type)

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, 양수로 표현한다고 한다.

0개의 댓글