[Swift] 확장 (Extension)

확장은 기존 클래스, 구조, 열거형 또는 프로토콜에 새로운 기능을 추가할 수 있다

기존 소스코드에 접근할 수 없는 타입들도 확장할 수 있다

Int, String 같은 기본 타입들도 확장할 수 있다

아래와 같은 기능을 추가할 수 있다.

  1. 인스턴스 / 타입 연산 프로퍼티
  2. 인스턴스 / 타입 메소드
  3. subscript 추가
  4. 중첩 타입의 정의와 사용
  5. 기존 타입에 프로토콜 적용

기능 추가는 가능하지만 override는 불가능하다

문법 (Syntax)

extension 타입명 { }

extension 키워드 뒤에 확장할 타입을 작성하여 선언

extension 타입명: 프로토콜1, 프로토콜2 ... { }

1개 이상의 프로토콜을 채택할 수 있다

확장을 사용하면 확장한 타입의 모든 인스턴스에서 확장에서 추가한 기능을 사용할 수 있다

즉, 확장이 정의되기전 생성된 인스턴스를 포함한 모든 인스턴스에서 사용이 가능하다

struct SomeStruct {
    var name: String
    var age: Int
}
let sweet = SomeStruct(name: "sweet", age: 20)
print(sweet.description)

extension SomeStruct: CustomStringConvertible {
    var description: String { " \(name) \(age) " }
}

위 코드에서 sweet 인스턴스는 확장을 하기 전에 생성된 인스턴스지만 확장에서 추가한 description 연산프로퍼티를 이상없이 사용할 수 있다.

연산 프로퍼티 추가

extension Double {
    var km: Double { self * 1000 }
    var m: Double { self }
    var cm: Double { self / 100 }
    var mm: Double { self / 1000 }
    var ft: Double { return self / 3.28084 }
}

let oneInch = 25.4.mm
println("One inch is \(oneInch) meters")
// prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
println("Three feet is \(threeFeet) meters")
// prints "Three feet is 0.914399970739201 meters"

기본 타입인 Double을 확장하여 5가지의 연산 프로퍼티를 추가함

확장은 연산 프로퍼티를 추가할 수 있지만, 저장 프로퍼티를 추가하거나 기존 프로퍼티에 옵저버를 추가할 수 없다

이니셜라이저 추가

다른 타입을 확장하여 사용자 정의 타입을 매개변수로 하거나 해당 타입에 정의되지 않은 추가적인 이니셜라이저 옵션을 제공할 수 있다

클래스를 확장하는 경우 편의 이니셜라이저를 추가할 수 있지만, 디이니셜라이저와

지정 이니셜라이저를 추가할 수 없다.

지정 이니셜라이저와 디이니셜라이저는 항상 원래 클래스에서 정의되야 한다.

초기화 해지는 클래스당 1개밖에 선언 안되니까 확장에서 할 수 없는건 당연하지 않을까??

class SomeClass {
    var someProperty: Int
    // 지정 이니셜라이저는 항상 본래의 정의부에서 선언
    init(_ p: Int) { }
    deinit { }
}
extension SomeClass { 
    convenience init() { } // 편의 이니셜라이저는 가능
    init() { } // 지정 이니셜라이저는 불가
    deinit { } // 초기화 해지 불가 
}

값 타입의 경우 모든 저장 프로퍼티가 기본값을 가지고 있고 사용자 정의 이니셜라이저가 정의되어 있지 않다면

확장을 사용하여 이니셜라이저를 추가할 때 해당 이니셜라이저 안에서 기본 이니셜라이저와

멤버와이즈 이니셜라이저를 사용할 수 있다.

만약 해당 값 타입에 이니셜라이저가 정의되어 있다면 해당 사항은 없다

struct SomeStruct {
    var someProperty = 0
    var someProperty2 = 0
}

extension SomeStruct {
    init(someParameter: Int) {
        init() // 호출 가능 ( 기본 이니셜라이저 )
        init(someProperty: 5, someProperty2: 10) // 호출 가능 ( 멤버 와이즈 이니셜라이저 )
    }
}
struct SomeStruct {
    var someProperty = 0
    var someProperty2 = 0
    init(some: Int, parameter: Int) { }
}

extension SomeStruct {
    init(someParameter: Int) {
        // 이 경우에는 사용자 정의 이니셜라이저가 있기 때문에 모두 호출 불가능
        init() 
        init(someProperty: 5, someProperty2: 10)
    }
}

다른 모듈에서 정의된 구조체를 확장하여 이니셜라이저를 추가하는 경우

확장안에서 추가하는 이니셜라이저에서는 기존 이니셜라이저를 호출하기 전에는 self로 접근이 불가하다

메소드 추가

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

let number = 3
number.repeating { print("hello") }
// hello
// hello
// hello

위 처럼 일반적인 메소드 추가하는 것과 같이 추가할 수 있다.

Mutating 메소드

값 타입에서는 메소드로 저장 프로퍼티를 변경 시 mutating 키워드를 사용해야 함

이는 확장에서도 동일하게 적용되고 지원함

extension Int {
    mutating func square() {
        self = self * self
    }
}

// 값의 변경이 이루어지기 때문에 var 사용
var num = 3
num.square()
print(num) // 9 

첨자 추가 (subscript)

struct SomeStruct {
    var name: String = "sweetdev"
    var age: Int = 20
}

extension SomeStruct {
    subscript(key: String) -> String {
        switch key {
        case "name":
            return name
        case "age":
            return String(age)
        default:
            return "invalid keyword"
        }
    }
}

let sweetdev = SomeStruct()
print(sweetdev["name"])
print(sweetdev["age"])
print(sweetdev["asdasd"])

// sweetdev
// 20
// invalid keyword

중첩 타입의 추가 (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
        }
    }
}

let num = [3,1,5,0,-2,0,-6]

for nums in num {
	print(nums.kind)
}
/*
positive
positive
positive
zero
negative
zero
negative
*/

태그:

카테고리:

업데이트:

댓글남기기