[Swift] 20. Extensions

도윤·2021년 8월 11일
0

Swift

목록 보기
18/21

Extension은 존재하는 클래스나 구조체,열거형, 프로토콜 타입에 새로운 기능을 더하는 것이다. 원래의 코드에서는 접근할 수 없는 타입을 추가시키는 기능이다.

  • 계산 타입 프로퍼티나 계산 인스턴스 프로퍼티 추가
  • 타입 메서드나 인스턴스 메서드 정의
  • 새로운 생성자 선언
  • subscripts 선언
  • 새로운 nested types 사용하거나 선언
  • 존재하는 타입을 프로토콜을 따르도록 함.

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


Extension Syntax

extension 키워드로 선언하면 된다.

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

extension은 존재하는 타입을 확장하여 하나 이상의 프로토콜을 따르게 할 수 있다. 프로토콜을 추가하는 것은 클래스나 구조체를 쓰는 것 처럼 프로토콜의 이름을 쓰면 된다.

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

extension은 존재하는 generic type을 연장하기 위해 사용되기도 한다.


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

계산 프로퍼티는 double 값이 특정 유닛의 길이로써 표현할 수 있다. 비록 계산 프로퍼티로 실행되더라도 이 프로퍼티의 이름들은 dot(.)과 함께 부동소수점 리터럴 값에 추가할 수 있다.

이러한 프로퍼티들은 read-only계산 프로퍼티이고 그들은 get 키워드없이 표현되어진다. 그들은 double type의 리턴값을 반환한다

let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// Prints "A marathon is 42195.0 meters long

Extension은 새로운 계산 프로퍼티를 추가할 수 있지만 저장 프로퍼티나 observer 프로퍼티는 추가할 수 없다.


Initializers

Extension은 기존 유형에 새 생성자를 추가할 수 있습니다. 이렇게 하면 다른 유형을 확장하여 사용자 정의 유형을 이니셜라이저로 사용할 수 있고, 타입의 기존의 실행의 부분으로써 포함되지 않는 추가적인 생성자를 제공할 수 있다.

Extension은 클래스에 편리 생성자를 더할 수 있지만, desiginated 생성자나 deinitializer을 클래스에 추가할 수 없다. Designated 생성자와 소멸자는 반드시 원래 클래스에 의해 제공되어야 한다.

Extension을 사용하여 저장된 모든 속성의 default 값을 제공하는 값 타입에 생성자를 추가하고 사용자 정의 생성자를 정의하지 않는 경우, 확장 생성자 내에서 해당 값 타입에 대한 기본 생성자 및 구성원별 생성자를 호출할 수 있습니다. 이는 값 유형의 Initializer 위임에 설명된 대로 값 유형의 원래 구현의 일부로 이니셜라이저를 작성한 경우에는 해당되지 않습니다.

확장을 사용하여 다른 모듈에서 선언된 구조에 이니셜라이저를 추가하는 경우 새 생성자는 정의 모듈에서 생성자를 호출할 때까지 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()
}

Rect 구조체는 모든 프로퍼티에 default value를 제공하고 있어서 default 생성자와 멤버별 생성자를 자동으로 수신한다.

let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
   size: Size(width: 5.0, height: 5.0))
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값에 기반한 원래의 포인트를 계산하므로써 생성된다.
생성자는 구조체의 멤버 생성자 init을 자동으로 호출한다.

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)

Methods

Extension은 새로운 인스턴스 메서드나 타입 메서드를 존재하는 타입에 추가할 수 있다.

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

repetition 메서드는 매개변수가 없고 값을 반환하지 않는 task를 매개변수로 받는다.

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

Mutating Instance Methods

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

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

Subscripts

Extension은 존재하는 타입에 새로운 Subscript를 추가할 수 있다.

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

만약 요청된 index에 충분한 digit이 존재하지 않으면 0을 반환한다.


Nested Types

존재하는 클래스,구조체,열거형에 nested type을 추가할 수 있다.

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 + "

number.kind는 Int 타입에 이미 존재하는 메서드이다. Int.kind의 모든 값은 Int.kind.nagative라기 보다는 .nagative처럼 간편히 쓸 수 있다.

0개의 댓글