익스텐션

피터·2022년 9월 14일
0
post-thumbnail

익스텐션이란

구조체, 클래스, 열거형, 프로토콜 타입에 새로운 기능을 추가할 수 있습니다. 기능을 추가하려는 타입을 구현한 소스코드를 알지 못하거나 볼 수 없다고 해도, 타입만 안다면 그 타입의 기능을 확장할 수도 있습니다.

익스텐션으로 타입에 추가할 수 있는 기능

  • 연산 타입 프로퍼티 / 연산 인스턴스 프로퍼티
  • 타입 메서드 / 인스턴스 메서드
  • 이니셜라이저
  • 서브스크립트
  • 중첩 타입
  • 특정 프로토콜을 준수할 수 있도록 기능 추가

익스텐션은 타입에 새로운 기능을 추가할 수는 있지만, 기존에 존재하는 기능을 재정의할 수는 없습니다.

클래스의 상속과 익스텐션

  • 상속
    • 클래스 타입에서만 가능
    • 특정 타입을 물려받아 하나의 새로운 타입을 정의 후 추가 기능 구현하는 수직 확장
    • 재정의 가능
  • 익스텐션
    • 구조체, 클래스, 프로토콜 등에 적용 가능
    • 기존 타입에 기능을 추가하는 수평 확장
    • 재정의 불가능

💡 익스텐션은 언제 유용할까?
다양한 경우에서 유용하지만 가장 필요한 경우는 외부 라이브러리를 사용해서 원본 소스를 수정하지 못할 경우 추가 기능을 사용하고자 할 때 유용하게 사용됩니다.

익스텐션 문법

extension 확장될 타입 이름 {
    타입에 추가될 새로운 기능 구현
}

익스텐션은 기존에 존재하는 타입에 다른 프로토콜을 추가로 채택할 수 있도록 확장할 수 있습니다.

extension 확장될 타입 이름: 프로토콜1, 프로토콜2, 프로토콜3 {
    프로토콜 요구 사항 구현
}

익스텐션으로 추가할 수 있는 기능

연산 프로퍼티

extension Int {
    var isEven: Bool {
        return self % 2 == 0
    }
    
    var isOdd: Bool {
        return self % 2 == 1
    }
}

익스텐션으로 연산 프로퍼티를 추가할 수 있지만, 저장 프로퍼티는 추가할 수 없습니다.

또, 타입에 저장되어 있는 기존의 프로퍼티에 프로퍼티 감시자를 추가할 수도 없습니다.

메서드

  • 인스턴스 메서드 / 타입 메서드 구현
extension Int {
    func multiplier(by n: Int) -> Int {
        return self * n
    }
		
		// 타입 메서드
		static func isIntInstance(_ instance: Any) -> Bool {
        return instance is Self
    }
}

let result = 3.multiplier(by: 4)
print(result)
// 12

print(Int.isIntInstance(4.6))
// false
  • 연산자 구현
struct Position {
    var x: Int
    var y: Int
}

extension Position {
    // 중위 연산자
    static func + (left: Position, right: Position) -> Position {
        return Position(x: left.x + right.x, y: left.y + right.y)
    }
    // 전위 연산자
    static prefix func - (current: Position) -> Position {
        return Position(x: -current.x, y: -current.y)
    }
    // 복합 할당 연산자
    static func += (left: inout Position, right: Position) {
        left = left + right
    }
    // 비교 연산자
    static func == (left: Position, right: Position) -> Bool {
        return (left.x == right.x) && (left.y == right.y)
    }
    
    static func != (left: Position, right: Position) -> Bool {
        return !(left == right)
    }
}

익스텐션 블록을 나누는 갯수의 제한은 없습니다. 그렇기 때문에 관련 기능별로 하나의 익스텐션 블록에 묶어주는 것도 좋습니다.

이니셜라이저

값 타입(구조체나 열거형 등)의 인스턴스를 초기화할 때, 타입의 정의 부분에 이니셜라이저를 추가하지 않더라도 익스텐션을 통해 이니셜라이저를 추가할 수 있습니다.

struct Position {
    var x: Int
    var y: Int
}

extension Position {
    init() {
        self.x = 0
        self.y = 0
    }
    
    init(x: Int) {
        self.x = x
        self.y = 0
    }
}

let initPosition = Position()
let xPosition = Position(x: 3)

print(initPosition)
print(xPosition)

// Position(x: 0, y: 0)
// Position(x: 3, y: 0)

하지만 클래스 타입의 경우 익스텐션으로 지정 이니셜라이저와 디이니셜라이저를 추가할 수 없습니다. 지정 이니셜라이저와 디이니셜라이저는 반드시 클래스 타입의 구현부에 위치해야 합니다. 다만 익스텐션에 편의 이니셜라이저는 추가가 가능합니다.

class Person {
    var name: String
    var age: Int
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

extension Person {
    convenience init(age: Int) {
        self.init(name: "이승철", age: 0)
        self.age = age
    }
}

let 이승철 = Person(age: 39)

print(이승철.age)

// 39

익스텐션을 통해 추가하는 이니셜라이저는 타입의 기존 이니셜라이저가 갖는 책무를 동일하게 수행해야 합니다. 즉, 이니셜러아저 호출이 종료되는 시점까지 인스턴스가 정상적으로 완벽하게 초기화되는 것을 책임져야 합니다.

서브스크립트

extension String {
    subscript(appendValue: Self) -> Self {
        return self + appendValue
    }
    
    subscript(repeatCount: Int) -> Self {
        var str = String()
        for _ in 0..<repeatCount {
            str += self
        }
        return str
    }
}

print("가나다"[4]) // 가나다가나다가나다가나다
print("마바사"["아자"]) // 마바사아자

중첩 데이터 타입

익스텐션을 통해 타입에 중첩 데이터 타입을 추가할 수 있습니다.

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
        }
    }
}

print(100.kind)
print(0.kind)
print((-4589).kind)

익스텐션을 통해 Int 타입에 Kind라는 열거형 타입과 Kind 타입의 연산 프로퍼티를 추가해주었습니다.

자료 출처: 야곰 스위프트 프로그래밍 3판

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."
profile
iOS 개발자입니다.

0개의 댓글