[Swift] 확장 (Extension)
확장은 기존 클래스, 구조, 열거형 또는 프로토콜에 새로운 기능을 추가할 수 있다
기존 소스코드에 접근할 수 없는 타입들도 확장할 수 있다
Int, String 같은 기본 타입들도 확장할 수 있다
아래와 같은 기능을 추가할 수 있다.
- 인스턴스 / 타입 연산 프로퍼티
- 인스턴스 / 타입 메소드
- subscript 추가
- 중첩 타입의 정의와 사용
- 기존 타입에 프로토콜 적용
기능 추가는 가능하지만 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
*/
댓글남기기