티스토리 뷰

반응형

안녕하세요 Pingu 입니다! 🐧

 

이번 글에서는 Swift 공식 문서의 20번째 단원인 Extensions을 읽고 정리한 글을 쓰려고 합니다.

 

Apple Swift 공식 문서 20단원 - Extensions

Extensions

Extensions(익스텐션)은 기존의 클래스, 구조체, 열거형, 프로토콜에 새로운 기능을 추가하는 기능입니다. 이는 실제로 해당 타입들이 정의된 코드에 접근할 수 없을 때에도 사용할 수 있어요.

 

Swift의 익스텐션은 다음을 수행할 수 있습니다.

  • 인스턴스의 계산 프로퍼티와 계산 타입 프로퍼티 추가
  • 인스턴스 메서드와 타입 메서드 정의
  • 새로운 생성자 제공
  • 서브 스크립트 정의
  • 새로운 중첩 타입 정의
  • 기존 유형이 특정 프로토콜을 준수하도록 만들기

Swift에서는 프로토콜을 익스텐션 해서 요구사항의 구현을 제공하거나 프로토콜에 추가 기능을 추가할 수도 있습니다. 익스텐션으로 타입에 새로운 기능을 추가할 수는 있지만 기존의 기능을 오버라이드 할 수는 없습니다.


Extension Syntax

익스텐션을 사용하기 위해선 extension키워드를 사용해야합니다.

extension SomeType {
    // new functionality to add to SomeType goes here
}

익스텐션은 기존 타입을 확장하여 하나 이상의 프로토콜을 채택할 수 있습니다. 이렇게 프로토콜을 채택하려면 클래스, 구조체에서 채택하는 방식과 동일한 방식으로 프로토콜 이름을 작성하면 됩니다.

 

extension SomeType: SomeProtocol, AnotherProtocol {
    // implementation of protocol requirements goes here
}

 

이렇게 프로토콜을 채택하는 방법은 Protocol단원에서 살펴볼 수 있습니다.

 

익스텐션은 존재하는 generic 타입에도 적용할 수 있습니다. Generic에 익스텐션을 적용하는 방법은 Generic에서 살펴볼 수 있습니다. 만약 익스텐션을 사용하여 기존 타입에 새로운 기능을 추가하면 추가하기 전에 생성된 인스턴스들도 새로운 기능을 사용할 수 있습니다.


Computed Properties

익스텐션은 존재하는 타입에 계산 프로퍼티, 계산 타입 프로퍼티를 추가할 수 있습니다.

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 타입에 5개의 추가적인 프로퍼티를 추가한 것입니다. 예제에서 정의한 프로퍼티들은 Double 타입의 값이 m 단위로 표현되도록 합니다. 이러한 프로퍼티들은 읽기 전용 계산 프로퍼티이므로 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

익스텐션으로 기존 타입에 새로운 생성자를 추가할 수 있습니다. 이를 통해 타입을 확장하여 커스텀 타입을 생성자의 매개변수로 받거나 원래 없던 생성자를 제공할 수 있습니다. 익스텐션은 클래스에 새로운 convenience 생성자는 추가할 수 있지만 designated 생성자나 소멸자는 추가할 수 없습니다. 이러한 부분은 항상 실제 클래스 구현부에서 정의되어야 합니다.

 

익스텐션을 사용하여 커스텀 생성자가 없는 타입 값 타입에 생성자를 추가한다면 추가하려는 생성자에서 모든 저장 프로퍼티에 기본값을 할당하면 해당 값 타입의 default 생성자와 memberwise 생성자를 사용할 수 있습니다. 이 경우 값 타입에 커스텀 생성자가 존재하는 경우에는 해당하지 않습니다.

 

익스텐션을 사용하여 다른 모듈에서 선언된 구조체에 생성자를 추가하려는 경우 새로운 생성자는 해당 구조체의 모듈에서 생성자를 호출할 때까지 self에 접근할 수 없습니다.

 

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

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
   size: Size(width: 5.0, height: 5.0))

위의 예제는 Rect 구조체와 이 구조체의 프로퍼티 타입으로 사용되는 Size, Point 구조체를 정의한 것입니다. Rect 구조체는 default 생성자와 memberwise 생성자로 모든 프로퍼티에 default값을 제공하기 때문에 위와 같이 새로운 인스턴스를 만들 수 있습니다.

 

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

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)

위의 코드는 Rect 구조체에 새로운 생성자를 추가하고 실제로 인스턴스를 만드는 코드입니다. 새로운 생성자는 center, size라는 매개변수를 받게 되고 center는 중심점 size는 크기를 나타냅니다. 그런 뒤 생성자는 memberwise 생성자를 자동으로 호출해서 새로운 값을 적절한 프로퍼티에 저장합니다. 이렇게 익스텐션으로 추가한 생성자도 초기화가 완료되면 인스턴스가 완전히 초기화되도록 해야 합니다.


Methods

익스텐션으로 존재하는 타입에 인스턴스 메서드나 타입 메서드를 추가할 수 있습니다.

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

3.repetitions {
    print("Hello!")
}
// Hello!
// Hello!
// Hello!

위의 코드는 이미 존재하는 Int 타입에 새로운 메서드를 추가한 것이다. repetitions(task:) 메서드는 매개변수가 없고 값을 반환하지 않는 함수 타입을 매개변수로 받습니다. 실제 사용 예를 보면 어떤 방식인지 이해할 수 있을 것 같아요.

 

Mutating Instance Methods

익스텐션으로 추가된 인스턴스 메서드는 인스턴스의 값들을 수정할 수도 있습니다. 구조체와 열거형의 메서드는 값 타입이므로 자체적으로 수정하기 위해서는 mutating 키워드를 함께 사용해서 메서드를 추가해야 합니다.

extension Int {
    mutating func square() {
        self = self * self
    }
}
var someInt = 3
someInt.square()
// someInt is now 9

Subscripts

익스텐션으로 기존의 타입에 새로운 서브 스크립트를 추가할 수 있습니다.

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 타입에 새로운 서브스크립트를 추가하는 코드입니다. 여기서 추가한 서브 스크립트의 기능은 어떠한 정수의 자릿수를 알려주는 기능을 합니다.

746381295[9]
// returns 0, as if you had requested:
0746381295[9]

즉 알아보려는 정수의 자릿수보다 더 큰 자릿수를 요청하면 0을 반환하게 됩니다.


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, 양수를 나타냅니다.

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 열거형을 사용하여 printIntegerKinds(_ :) 함수를 정의합니다. 이 함수는 어떠한 정수에 대하여 음수면 -, 양수면 +, 0이면 0을 출력한다. 이를 사용해보면 위와 같이 잘 실행되는 것을 볼 수 있습니다.

 

감사합니다!

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 31
글 보관함