728x90
반응형

인스턴스를 생성하는 이니셜라이저와 인스턴스가 소멸될 때 호출되는 디이니셜라이저에 대해서 공부하려고 한다.
클래스나 구조체 등을 만들때 프로퍼티의 값을 할당해주었던 것을 생각해보자.
저장 프로퍼티에 값을 할당하지 않으면 에러가 발생한다.
그래서 인스턴스를 생성함과 동시에 초기값을 따로 할당해서 사용했다.
그런데 초기화와 동시에 프로퍼티의 값을 할당하고 싶을 때 이니셜라이저를 사용할 수 있다.

프로퍼티 초기값

스위프트의 모든 인스턴스는 초기화와 동시에 모든 프로퍼티에 유효한 값이 할당되어야 한다.
프로퍼티에 미리 기본 값을 할당해두면 인스턴스가 생성됨과 동시에 초기값을 지니게 된다.
다음 예를 통해 확인할 수 있다.

class PersonPropertyInit {
    // 모든 저장 프로퍼티에 기본값 할당
    var name: String = "unknown"
    var age: Int = 0
    var nickName: String = "nick"
}

// 인스턴스 생성
let je: PersonPropertyInit = PersonPropertyInit()

// 미리 기본값을 할당해두었기 때문에 인스턴스가 생성되면서 초기값을 가져온다.
print("이름 : \(je.name) , 나이 : \(je.age) , 별명 : \(je.nickName)")

// 기본값이 인스턴스가 지녀야 할 값과 맞지 않다면
// 생성된 인스턴스의 프로퍼티에 각각 값 할당
je.name = "je"
je.age = 28
je.nickName = "j2"

// 다시 각각의 값을 할당해주고 출력해보았다.
print("이름 : \(je.name) , 나이 : \(je.age) , 별명 : \(je.nickName)")

----------- 출력 결과 ----------
이름 : unknown , 나이 : 0 , 별명 : nick
이름 : je , 나이 : 28 , 별명 : j2

클래스를 생성하고 각각의 저장 프로퍼티에 기본값을 할당해주었다.
그리고 하나의 인스턴스를 생성하고 프로퍼티의 값을 확인해보았다.
기본값으로 할당되어 있는 값이 출력된 것을 확인할 수 있었다.
그리고 생성된 인스턴스의 프로퍼티의 값이 맞지 않다면 각 프로퍼티의 값을 다시 할당해줄 수 있다.
바꾼 후 출력을 다시 확인해보면 바뀐 것을 볼 수 있었다.

이니셜라이저 | initializer

프로퍼티의 초기값을 지정하기 어려운 경우에는 이니셜라이저 init 을 통해 인스턴스가 가져야 할 초기값을 전달할 수 있다.
다음 예를 통해서 이니셜라이저를 사용해보았다.

class PersonInitializer {
    // 저장 프로퍼티의 기본 값을 할당하지 않는다.
    var name: String
    var age: Int
    var nickName: String
    
    // 이니셜라이저
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.age = age
        self.nickName = nickName
    }
}

// 인스턴스를 생성함과 동시에 이니셜라이저를 통해서 프로퍼티의 값을 할당해주고 있다.
let su: PersonInitializer = PersonInitializer.init(name: "su", age: 27, nickName: "수수")

// 이니셜라이저에서 할당한 값이 출력되는 것을 확인할 수 있다.
print("이름 : \(su.name) , 나이 : \(su.age) , 별명 : \(su.nickName)")

========== 출력 결과 ==========
이름 : su , 나이 : 27 , 별명 : 수수

아까와는 달리 프로퍼티의 기본값을 할당해주지 않고
인스턴스를 생성 후 이니셜라이저를 통해서 초기값을 할당해줄 수 있다.

만약, 프로퍼티의 초기값이 필요없다면 옵셔널을 사용하면 된다.

class PersonInitializerException {
    var name: String
    var age: Int
    // 옵셔널을 사용해서 이니셜라이저에서 초기값을 할당하지 않게 한다.
    var nickName: String?
    
    // 이니셜라이저
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.age = age
        self.nickName = nickName
    }
    
    // 위와 동일한 기능을 수행한다.
    //convenience init(name: String, age: Int, nickName: String) {
    // 클래스 내부의 이니셜라이저를 사용하려면 convenience 라는 키워드를 사용해줘야 한다.
    //    self.init(name: name, age: age)
    //    self.nickName = nickName
    //}
    
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}


let ju: PersonInitializerException = PersonInitializerException.init(name: "ju", age: 27, nickName: "주주")
let du: PersonInitializerException = PersonInitializerException.init(name: "du", age: 22)

print("이름 : \(ju.name) , 나이 : \(ju.age) , 별명 : \(ju.nickName)")
print("이름 : \(du.name) , 나이 : \(du.age) , 별명 : \(du.nickName)")

========== 출력 결과 ==========
이름 : ju , 나이 : 27 , 별명 : Optional("주주")
이름 : du , 나이 : 22 , 별명 : nil

nickName 이라는 프로퍼티를 옵셔널 타입으로 선언하고 이니셜라이저를 두 개 만들었다.
nickName 의 초기값이 꼭 필요하지 않다면 옵셔널을 사용해서 이니셜라이저를 생성하면된다.
결과를 보면 알 수 있듯이 초기값을 할당해주지 않았기 때문에 nil 의 값이 할당되는 것을 확인할 수 있다.
그리고 nickName 의 초기값을 할당해주면 옵셔널 타입의 값이 입력된 것을 확인할 수 있었다.

그리고 인스턴스를 사용할 때 꼭 필요하지만 초기값을 할당하지 않을 때 암시적 추출 옵셔널을 사용하면 된다.
다음의 예를 통해 확인해볼 수 있다.

class Puppy {
    var name: String
    var owner: PersonInitializerException!
    
    init(name: String) {
        self.name = name
    }
    
    func goOut() {
        print("\(name) 가 주인 \(owner.name) 와 산책을 합니다.")
    }
}

let coco: Puppy = Puppy(name: "coco")

// 주인이 없는 상태라서 오류가 발생한다.
// coco.goOut()

coco.owner = ju
coco.goOut()

========== 출력 결과 ==========
coco 가 주인 ju 와 산책을 합니다.

Puppy 라는 클래스를 생성하고 인스턴스를 생성한다.
인스턴스를 생성할 때 owner 라는 프로퍼티가 존재해야 하지만 초기값을 할당하지 않는다.
그래서 초기값을 할당하지 않으면 출력할 때 owner 의 값이 없기 때문에 에러가 발생한다.

그래서 onwer 에 값을 할당해주면 에러 없이 출력하는 것을 확인할 수 있었다.

실패 가능한 이니셜라이저

이니셜라이저 매개변수로 전달되는 초기값이 잘못된 경우 인스턴스 생성에 실패할 수 있다.
인스턴스 생성에 실패하면 nil 을 반환한다.
실패가능한 이니셜라이저의 반환타입은 옵셔널 타입이다.
init? 을 사용한다.

class PersonFailInitializer {
    var name: String
    var age: Int
    var nickName: String?
    
    init?(name: String, age: Int) {
        // 나이의 값이 0~120 에 포함되지 않으면 nil 로 반환된다.
        if (0...120).contains(age) == false {
            return nil
        }
        
        // 이름이 없으면 nil 반환
        if name.count == 0 {
            return nil
        }
        
        self.name = name
        self.age = age
    }
}

let john: PersonFailInitializer? = PersonFailInitializer(name: "john", age: 23)
let jake: PersonFailInitializer? = PersonFailInitializer(name: "joker", age: 130)
let bat: PersonFailInitializer? = PersonFailInitializer(name: "", age: 35)

print(jake)
print(bat)

========== 출력 결과 ==========
nil
nil

jake 는 나이가 120살을 벗어나 nil 값을 반환했고 bat 은 이름이 없어서 nil 값을 반환했다.
인스턴스를 생성하고 초기값을 잘못 할당해주었을 때 nil 이 반환되는 것을 확인할 수 있었다.

디이니셜라이저 | deinitializer

deinit 은 클래스의 인스턴스가 메모리에서 해제되는 시점에 호출된다.
인스턴스가 해제되는 시점에 해야할 일을 구현할 수 있다.
deinit 은 매개변수를 지닐 수 없다.
자동으로 호출되므로 직접 호출할 수 없다.
디이니셜라이저는 클래스 타입에만 구현할 수 있다.
인스턴스가 메모리에서 해제되는 시점은 ARC(Automatic Reference Counting) 의 규칙에 따라 결정된다.

class PersonDeinit {
    var name: String
    var pet: Puppy?
    var child: PersonInitializerException
    
    init(name: String, child: PersonInitializerException) {
        self.name = name
        self.child = child
    }

    // 인스턴스가 해제되는 시점에서 자동으로 호출된다.
    deinit {
        if let petName = pet?.name {
            print("\(name) 가 \(child.name) 에게 \(petName) 를 인도합니다.")
        }
    }
}

var dol: PersonDeinit? = PersonDeinit(name: "doldol", child: du)
dol?.pet = coco

// 인스턴스가 사라지면서 deinit 가 자동으로 호출된다.
dol = nil

========== 출력 결과 ==========
doldol 가 du 에게 coco 를 인도합니다.

클래스를 만들고 나서 인스턴스를 생성한 후 nil 값을 할당해서 인스턴스를 해제했다.
해제하면서 디이니셜라이저가 자동으로 호출되면서 해당 결과를 출력했다.

 

클래스나 구조체 그리고 열거형을 사용할 때 저장 프로퍼티의 값을 할당해서 사용했었다.
인스턴스를 생성하고 프로퍼티의 초기값을 바로 할당해주어 사용했었는데
이니셜라이저를 사용하므로써 인스턴스의 생성과 동시에 프로퍼티의 초기값을 할당할 수 있다는 점
불필요한 코드를 줄일 수 있는 것도 좋은 점인 것 같다.
그리고 디이니셜라이저는 클래스에서만 사용할 수 있는 걸 잘 알아줘야겠다.

 

- 참고 사이트 - 

www.edwith.org/boostcamp_ios/lecture/11310

 

[LECTURE] 22. 인스턴스 생성 / 소멸 (init / deinit) : edwith

:: 인스턴스의 생성과 소멸 :: - init/deinit - 인스턴스를 생성하는 이니셜라이저와 클래스의 인스턴스가 소멸될 때 호출되는 디이니셜라이저, 그리고 이와 관련된 것들에 ... - 부스트코스

www.edwith.org

728x90
반응형
복사했습니다!