extension
이란 키워드를 사용하여 기존 클래스, 구조체, 열거형 타입에 새로운 Property, Method, Initializer 등을 확장하는 것
원본 타입에 접근하지 못하는 타입들도 확장해서 사용할 수 있다.
class Int {
}
// 프로토콜을 이용한 확장
protocol SomeProtocol {
}
class SomeClass {
}
extension SomeClass: SomeProtocol {
}
저장 프로퍼티는 추가할 수 없고, 연산 프로퍼티만 추가 가능하다.
// 저장 프로퍼티 추가 불가능
extension String {
var aString = "a" // Error: Extensions must not contain stored properties
}
// 연산 프로퍼티 추가 가능
extension String {
var upperString: String {
get {
return self.uppercased()
}
}
}
let string = "a"
let aString = string.upperString
print(aString) // 출력값: A
인스턴스 메서드, 타입 메서드 모두 추가 가능하다.
extension String {
static func printB() {
print("B")
}
func printC() {
print("C")
}
}
// 타입 메서드
String.printB() // 출력값: B
// 인스턴스 메서드
let string = ""
string.printC() // 출력값: C
extension String {
subscript(idx: Int) -> String? {
guard (0..<count).contains(idx) else {
return nil
}
let target = index(startIndex, offsetBy: idx)
return String(self[target])
}
}
let string = "Hello, Swift!"
print(string[0]) // 출력값: Optional("H")
print(string[100]) //출력값: nil
Designated Initializer
: 클래스의 모든 속성(슈퍼 포함)을 초기화 하는 생성자
클래스의 모든 프로퍼티가 초기화 될 수 있도록 해줘야한다.
Convenience initializer
: 클래스의 원래 생성자인 Designated init을 도와주는 역할을 하는 보조 생성자
Designated init의 파라미터 중 일부를 기본값으로 설정해서
convenience init안에서 Designated init을 호출하여 초기화를 진행할 수 있다.
(convenience init을 사용하려면 Designated init이 꼭 먼저 선언되어야한다.)
class Person {
var name: String
var age: Int
var gender: String
// Designated Initializer
init(name: String, age: Int, gender: String) {
self.name = name
self.age = age
self.gender = gender
}
// Convenience initializer
// 파라미터로 넘겨주지않은 값은 임의로 지정하고 파라미터로 넘어간 것들만 넣어주면 된다.
convenience init(name: String, gender: String) {
self.init(name: name, age: 19, gender: gender)
}
}
Designated Initializer, deinitializer는 확장에서 추가 불가능하고
Convenience initializer만 추가할 수 있다.
// Designated Initializer
extension String {
init() { }
// Error: 'self.init' isn't called on all paths before returning from initializer
}
// deinitializer
extension String {
deinit {
print("String deinit")
} // Error: Deinitializers may only be declared within a class or actor
}
// Convenience initializer
// Convenience initializer는 최종적으로 Designated Initializer를 호출해야한다.
extension String {
init(name: String) {
self.init(name)
}
}
let myString = String(name: "Hello, World!")
print(myString) // 출력값: Hello, World!
extension으로 생성자를 추가할 경우,
Memberwise Initializer를 보존하며 새로운 생성자를 추가할 수 있다.
Memberwise Initializer
: 클래스와 달리 구조체에서만 제공하는 기본 생성자구문
// 구조체는 멤버와이즈 초기화 구문이 제공되기 때문에 프로퍼티를 생성하고 초기값을 지정해주지 않아도 된다.
struct Person {
var age: Int
var friend: Int
}
let p = Person(age: 19, friend: 5)
// 생성자를 직접 구현하면, 멤버와이즈 초기화 구문은 더이상 제공되지 않는다.
struct Person {
var age: Int
var friend: Int
init(value: Int) {
self.age = value
self.friend = value
}
}
let p = Person(value: 19)
let p = Person(age: 19, friend: 5) // Error
// extension을 사용하면, 멤버와이즈 초기화 구문은 유지하면서 새로운 생성자를 추가할 수 있다.
struct Person {
var age: Int
var friend: Int
}
// 멤버와이즈 초기화 구문 사용
let p = Person(age: 19, friend: 5)
extension Person {
init(value: Int) {
self.age = value
self.friend = value
}
}
// 직접 정의한 생성자 구문 사용
let p2 = Person(value: 19)