확장(Extensions)

  • 기존 클래스, 구조체, enum, protocol새로운 기능을 추가하는 것을 말한다. 기존 소스코드에서 접근할 수 없는 타입을 확장하는 기능도 추가된다.(retroactive modeling)
  • Objective-C의 categories와 유사하다. Swift에서는 name을 가지지 않는다.
  • Swift에서 확장이 하는 일은

    • computed 프로퍼티와 computed 타입 프로퍼티를 추가
    • 인스턴스 메소드와 타입 메소드를 정의
    • 새로운 초기화 제공
    • subscripts 정의
    • 새로운 nested 타입을 정의하고 사용
    • 프로토콜에 대해서 기존 타입 conform 만들기
  • 주의 : 새로운 기능을 타입에 추가하지만 기존 기능을 오버라이드할 수는 없다.

확장 문법

  • extension 키워드를 사용한다.
extension SomeType {
}
  • 기존 타입이 1개 이상의 프로토콜을 채택할 수 있게 확장할 수 있다. 프로토콜 이름은 클래스나 구조체에서와 같은 방식으로 작성한다.
extension SomeType: SomeProtocol, AnotherProtocol {
    //여기에 프로토콜을 만죽시키는 구현 작성
}
  • 주의: 새로운 기능을 기존 타입에 추가하기 위해서 extension을 정의하는 경우라면 새로운 기능은 해당 타입의 기존 모든 인스턴에서 가능하다. 심지어 extension이 정의되기 전에 생성된 것도 해당된다.

    Computed 프로퍼티

    • extension은 computed 인스턴스 프로퍼티와 computed 타입 프로퍼티를 기존 타입에 추가할 수 있다. 이 예제에서 5개 computed 인스턴스 프로퍼티를 Swift의 빌트인 Double 타입에 추가하여, 거리 단위와 관련도니 일을 처리하도록 지원한다.
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.4mm
print("One inch is \(oneInch) meters")

let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
  • 3개 computed 프로퍼티는 Double 값이 길이를 나타내는 단위로 사용했다는 것을 보여준다. 비록 computed 프로퍼티로 구현했지만 이 프로퍼티의 이름은 . 문법으로 소수점 리터럴로 추가할 수 있다. 이 리터럴은 거리를 변환하는데 사용한다.
  • 이 예제에서 1.0인 Double 값은 1 미터를 나타내는데 사용한다. 이는 m computed 프로퍼티가 self를 반환하는 이유이기도 하다. 1.m이라는 ㅛ현은 1.0의 Double 값을 계산하는 것을 의미한다.
  • 다른 단위는 미터 단위로 변환하기 위해서 변환이 필요하다. 1 킬로미터는 1,000미터와 같다. 따라서 km computed 프로퍼티는 변환을 위해서 1_000.00 값을 곱한다. 1미터에 3.28084 ft이므로 ft computed 프로퍼티는 3.28084로 Double 값을 나눠서 ft에서 미터로 변환이 가능하다.
  • 이 프로퍼티들은 읽기전용 computed 프로퍼티이므로 get 키워드 없이도 표현할 수 있다. 반환값은 Double 타입이고 Double이 가능한 어떤 수학적 계산에 사용할 수 있다.
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
  • 주의: Extension은 새로운 computed 프로퍼티를 추가할 수 있지만, stored 프로퍼티나 프로퍼티 오브져버를 기존 프로퍼티에 추가할 수는 없다.

초기화

  • 새로운 초기화를 기존 타입에 추가할 수도 있다. 이렇게 하면 초기화 파라미터로 여러분이 직접 작성한 타입을 받을 수 있게 타입을 확장할 수 있다. 그리고 원본 타입의 구현의 일부로 추가히지 않고도 추가적으로 초기화 옵션을 제공한다.
  • 클래스에 새로 편하게 초기화를 추가하지만, 새로 지정한 초기화나 deinitializer를 추가할 수는 없다. 지정한 초기화와 deinitializer는 반드시 원본 클래스에 구현해야만 한다.
  • 주의 값타입에 초기화를 추가하기 위해 extension을 사용한다면, 확장 초기화에서 기본 초기화나 멤버 초기화를 호출할 수 있다.
  • 아래 예제에서 정의한 Rect 구조체로 기하학적인 사각형을 나타낸다. Size와 Point라는 2개 구조체를 지원한다. 이것들은 속성으로 0.0ㅇㄹ 기본값으로 제공한다.
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 구조체는 모든 프로퍼티에 대해서 기본 값을 제공하므로, 자동으로 기본 초기화와 프로퍼티와 관련된 초기화가 적용된다. 이 초기화로 새로운 Rect 인스턴스를 생성할 수 있다.
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
  • Rect 구조체를 확장하여 특정 center point와 size를 가지는 초기화를 추가할 수 있다.

    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 값이 제공하는 값을 바탕으로 origin 포인트를 적절하게 계산한다. 다음으로 초기화는 구조체의 자동으로 멤버 프로퍼티를 초기화하는 init를 호출한다. 이것으로 새로운 origin과 size 값을 적절하게 각 프로퍼티에 저장하게 된다.

let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
  • 주의: 여러분이 직접 extension으로 새로운 초기화를 제공하면, 초기화가 마치고나서 각 인스턴스가 완전히 초기화되었는지 여부에 대한 책임을 여러분이 져야한다.

메소드

  • extention으로 새로운 인스턴스 메소드와 타입 메소드를 기존 타입에 추가할 수 있다. 다음 에제에서 repetitions라는 새로운 인스턴스 메소드를 추가할 것이다.
extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}
  • repetitions 메소드는 타입이 ()->Void인 단일 인자를 가지고 이것은 파라미터가 없고 반환값이 없는 함수를 말한다.
  • extention을 정의한 후에, 정수 횟수만큼 task를 수행하는 정수값에 repetitions 메소드를 호출할 수 있다.
3.repetitions({
    print("Hello!")
})
  • 클로저문법을 통해 더욱 간결한 호출이 된다.
3.repetitions{
    print("Goodbye!")
}

뮤테이팅 인스턴스 메소드

  • extension을 가지는 인스턴스 메소드는 인스턴스 자체를 변경할 수 있다. 구조체와 enum 메소드는 self를 변경하거나 해당 프로퍼티들은 반드시 mutating 키워드로 인스턴스 메소드에 표시를 해야한다. 원본 구현에서 변경한 메소드라는 것을 뜻한다.
  • 아래 예제에는 새로운 뮤테이팅 메소드 square를 Int 타입에 추가한다. 원래 값을 제곱한다.
extension Int {
    mutating func sqaure() {
        self = self * self
    }
}
var someInt = 3
someInt.square()

Subscript

  • 기존 타입에 extension은 새로운 subscript을 추가할 수 있다. 아래 예제에서는 정수 subscript를 Swift의 빌트인 Int 타입에 추가한다. 여기서 subscript [n]은 숫자의 오른쪽을 기준으로 n번째 위치한 수를 반환한다.
123456789[0]는 9를 반환한다.
123456789[1]는 8일 반환한다.
extension Int {
    subscript(var digitIndex: Int) -> Int {
            var decimalBase = 1
            while digitIndex > 0 {
            decimalBase *= 10
            --digitIndex
        }
        return (self / decimalBase) % 10
    }
}

746381295[0]
746381295[1]
746381295[2]
746381295[8]
  • 요청한 인덱스만큼 자릿수를 지원하지 않는 경우 subscript는 0을 반환한다. 마치 왼쪽으로 0을 추가한 것과 같은 동작이다.
746381295[9]

0746381295[9]

Nested 타입

  • 새로운 nested 타입을 기존 클래스, 구조체, enum에 추가할 수 있다.
  • 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 타입에 새로운 nested enum을 추가했다. Kind라는 enum으로 정수가 어떤 종류인지를 표현하는데 음수, 0, 양수인지를 나타낸다.
  • 다음 예제에서는 Int 타입에 새로운 computed 인스턴스 프로퍼티 kind를 추가한다. 이것은 정수에 대해서 Kind enum의 case를 반환한다.
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])
  • 함수 printIntegerKinds는 Int 값의 배열을 입력으로 받아서 차례로 사용한다. 배열에서 각 정수에 대해서 이 함수는 kind computed 프로퍼티로 여기고 적절한 설명을 출력한다.
  • 주의: number.kind는 이미 Int.Kind 타입으로 쓰이고 있다. 이런 이유 때문에, Int.Kind의 모든 case 값에 대해서 switch 내부에서 단축형으로 쓴다.(Int.Kind.Negative 대신에 .Negative를 사용)

정의

사용용도

문법

예제

Quiz

  • 정의에 대한 질문
  • 용도에 대한 질문
  • 예제 관련