Swift - Struct / Class (7_extension)

이한솔·2023년 9월 9일
0

Swift 문법 🍎

목록 보기
19/32

extension

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

subscript 추가

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)    
        
    }
    
}

Class 생성자 추가

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!

Struct 생성자 추가

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)

0개의 댓글