chaneeii / iOS-Study-Log

✨ iOS에 대해 공부한 것들을 기록합니다 ✨
18 stars 0 forks source link

[Initializer 1탄] 인스턴스 생성 및 소멸 #31

Open chaneeii opened 2 years ago

chaneeii commented 2 years ago

인스턴스 생성 및 소멸

야곰 스위프트 프로그래밍 Ch11 참고

1. 인스턴스의 생성

struct someStruct { init() {

}

}

enum SomeEnum { case someCase

init() {
    // 💬 열거형은 초기화할때 반드시 case 중 하나가 되어야합니다.
    self = .someCase
}

}

### 1.1 프로퍼티 기본값 (default value)
- 이니셜라이저가 실행될때 저장 프로퍼티에 적절한 초기값을 할당해야한다. (초기화 후에 값이 확정되지 않은 프로퍼티는 존재 x)
- 프로퍼티를 정의할 때 프로퍼티 기본값(default value)을 할당하면 이니셜라이저에서 따로 초기활르 하지 않아도 된다
  -> 프로퍼티의 기본값으로 저장 프로퍼티의 값이 초기화 된다.

> **초기화와 프로퍼티 감시자**
> 이니셜라이저를 통해 초깃값 할당하거나 프로퍼티 기본값을 통해 처음의 저장 프로퍼티가 초기화 될때는 프로퍼티 감시자 메소드가 호출안됨

```swift
struct someStruct {
    var someVar: Int
    init() {
        someVar = 2 //초기값할당
    }
}

struct someStruct {
    var someVar: Int = 2
}

1.2 이니셜라이저 매개변수

이니셜라이져도 매개변수를 가질 수 있다.

}


### 1.3 옵셔널 프로퍼티 타입
아래와 같은 저장 프로퍼티는 옵셔널로 선언해도 된다.
- 초기화 과정에서 값을 초기화 하지 않아도 되는, 즉 인스턴스가 사용되는 동안에 값을 꼭 갖지 않아도 되는 저장 프로퍼티
- 초기화 과정에서 값을 지정해주기 어려운 저장 프로퍼티

옵셔널로 선언한 저장 프로퍼티를 초기화 과정에서 값을 할당해주지 않는다면 자동으로 `nil` 이 할당된다!
```swift
class Person {
    var name: String
    var age: Int?

    init(name: String) {
        self.name = name
    }
}

1.4 상수 프로퍼티

class Person {
    let name: String
    var age: Int?

    init(name: String) {
        self.name = name
    }
}

let avery: Person = Person(name: "avery")
avery.name = "chanhee" // 오류

1.5 기본이니셜라이저와 멤버와이즈이니셜라이저

지금까지는 사용자정의이니셜라이저 에 대해 설명했다. 만약 사용자정의이니셜라이저를 정의해주지 않는다면 ? -> 클래스나 구조체는 모든 프로퍼티에 기본값이 지정되어 있다는 전제하에 기본 이니셜라이저를 사용한다. => 즉, 기본이니셜라이저는 프로퍼티기본값으로 프로퍼티 초기화 하여 인스턴스를 생성 ✨

위의 내용을 정리하면

💨 기본이니셜라이저 : 저장프로퍼티의 기본값이 모두 지정되어 있고, 동시에 사용자정의 이니셜라이저가 정의되어 있지 않은 상태에서 제공

근데, 저장프로퍼티 선언할때 기본값이 없으면 이니셜라이저에 초기값을 설정해야하는데 프로퍼티 하나 때문에 매번 이니셜라이저를 추가하고 변경하는 것은 일이다 -> 그래서 등장한게 멤버와이즈이니셜라이저! (근데 구조체에만 있음!)

멤버와이즈이니셜라이져는 구조체에서 사용자정의이니셜라이저를 구현하지 않으면 프로퍼티의 이름으로 매개변수를 갖는 이니셜라이저이다. !! 클래스는 지원하지 않는다는게 포인트! 이것은 구조체만 가능합니다!

스크린샷 2022-08-26 오후 9 29 46

1.6 초기화 위임

self.init 을 이용하면 값타입에서 이니셜라이저가 다른 이니셜라이저를 호출할 수 있다. => self.init 은 당연히 이니셜라이저안에서만 사용이 가능한데, self.init 을 사용한다는 것 자체가 사용자정의 이니셜라이저를 사용한다는 것을 의미한다 => 결국 초기화 위임을 하려면 최소 2개 이상의 사용자 정의 이니셜라이저를 정의해야한다.

enum Student {
    case elementary, middle, high
    case none

    // 사용자정의 이니셜라이저가 있는 경우, init() 메서드를 구현해주어야 기본 이니셜라이저를 사용할 수 있습니다.
    init() {
        self = .none
    }

    init(koreanAge: Int) {          // 첫 번째 사용자정의 이니셜라이저
        switch koreanAge {
        case 8...13:
            self = .elementary
        case 14...16:
            self = .middle
        case 17...19:
            self = .high
        default:
            self = .none
        }
    }

    init(bornAt: Int, currentYear: Int) {   // 두 번째 사용자정의 이니셜라이저
        self.init(koreanAge: currentYear - bornAt + 1)
    }
}

var younger: Student = Student(koreanAge: 16)
print(younger)  // middle

younger = Student(bornAt: 1998, currentYear: 2016)
print(younger)  // high

1.7 실패가능한 이니셜라이저

이니셜라이저를 초기화할때 인스턴스를 초기화할수없는 여러가지 예외상황에서 사용가능하다

실패가능한 이니셜라이져 (Failable Initializer) : 초기화 실패 가능성을 내포한 이니셜라이저

  • 클래스, 구조체, 열거형 등에 모두 정의가능하다.
  • 실패시 nil 을 반환 -> 옵셔널로 지정된다. => init? 키워드 사용
  • 특히 열거형에서 유용 -> 특정 case 에 맞지 않는 값이 들어오면 생성에 실패할 수 있다.
// 코드 11-9 실패 가능한 이니셜라이저
class Person {
    let name: String
    var age: Int?

    init?(name: String) {

        if name.isEmpty {
            return nil
        }
        self.name = name

    }

    init?(name: String, age: Int) {
        if name.isEmpty || age < 0 {
            return nil
        }
        self.name = name
        self.age = age
    }
}

let yagom: Person? = Person(name: "yagom", age: 99)

if let person: Person = yagom {
    print(person.name)
} else {
    print("Person wasn’t initialized")
}
// yagom

let chope: Person? = Person(name: "chope", age: -10)

if let person: Person = chope {
    print(person.name)
} else {
    print("Person wasn’t initialized")
}
// Person wasn’t initialized

let eric: Person? = Person(name: "", age: 30)

if let person: Person = eric {
    print(person.name)
} else {
    print("Person wasn’t initialized")
}
// Person wasn’t initialized

열거형에서 활용

enum Student: String {
    case elementary = "초등학생", middle = "중학생", high = "고등학생"

    init?(koreanAge: Int) {
        switch koreanAge {
        case 8...13:
            self = .elementary
        case 14...16:
            self = .middle
        case 17...19:
            self = .high
        default:
            return nil
        }
    }

    init?(bornAt: Int, currentYear: Int) {
        self.init(koreanAge: currentYear - bornAt + 1)
    }
}

var younger: Student? = Student(koreanAge: 20)
print(younger)  // nil

younger = Student(bornAt: 2020, currentYear: 2016)
print(younger)  // nil

younger = Student(rawValue: "대학생")
print(younger)  // nil

younger = Student(rawValue: "고등학생")
print(younger)  // high

1.8 함수를 사용한 프로퍼티 기본값 설정


⚠️ 주의 : 클로저의 실행시점은 초기화할때 인스턴스의 다른 프로퍼티 값이 설정되기 전이다.

// 코드 11-11 클로저를 통한 프로퍼티 기본값 설정
class SomeClass {
    let someProperty: SomeType = {
        // 새로운 인스턴스를 생성하고 사용자정의 연산을 통한 후 반환해줍니다.
        // 반환되는 값의 타입은 SomeType과 같은 타입이어야 합니다.
        return someValue
    }**()**  // ✅ 클로저 뒤에 소괄호가 붙는 이유는 클로저를 실행하기 위해서다. -> 없다면 그냥 클로저 그자체임.
}

2. 인스턴스 소멸

클래스 의 인스턴스는 디이니셜라이저 (deinitializer / deinit ) 를 구현할 수 있다. -> 메모리에서 해제되기 직전 클래스 인스턴스와 관련하여 원하는 정리 작업 구현 가능

특징

var instance: SomeClass? = SomeClass() instance = nil // Instance will be deallocated immediately