[Swift] 인스턴스의 생성(init)과 소멸(deinit)

김형근·2024년 7월 21일

[Swift] 문법

목록 보기
13/20

🍎 [Swift] 인스턴스의 생성과 소멸

  • Swift에서 인스턴스를 생성하고 소멸시키는 과정에서 'init''deinit' 을 사용합니다.
  • 객체지향 프로그래밍에서 중요한 역할.

🍏 프로퍼티 기본값

// 모든 저장 프로퍼티에 기본값 할당
class PersonA {
    var name: String = "unknown"  // 기본값 "unknown"을 가진 프로퍼티
    var age: Int = 0               // 기본값 0을 가진 프로퍼티
    var nickName: String = "geuni" // 기본값 "geuni"를 가진 프로퍼티
}

let jason: PersonA = PersonA()
jason.name = "jason"
jason.age = 30
jason.nickName = "j"

프로퍼티 기본값: PersonA 클래스는 인스턴스가 생성될 때 기본값을 할당받습니다. 이렇게 하면 인스턴스는 모든 프로퍼티에 유효한 값을 가지게 됩니다. 기본값을 통해 초기화 과정이 간소화됩니다.


🍏 이니셜라이저

class PersonB {
    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 hana: PersonB = PersonB(name: "hana", age: 20, nickName: "하나")

이니셜라이저(init): 'PersonB' 클래스는 이니셜라이저를 통해 인스턴스를 생성할 때 프로퍼티의 값을 설정합니다. 이 방법은 프로퍼티에 기본값을 제공하기 어려운 경우 유용하며, 인스턴스 생성 시 필요한 값을 반드시 제공해야 합니다.


🍏 프로퍼티의 초기값이 꼭 필요 없을 때 (옵셔널 사용)

class PersonC {
    var name: String
    var age: Int
    var nickName: String? // 옵셔널 프로퍼티
    
    // 편의 이니셜라이저
    convenience init(name: String, age: Int, nickName: String) {
        self.init(name: name, age: age)
        self.nickName = nickName
    }
    
    // 기본 이니셜라이저
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let jenny: PersonC = PersonC(name: "jenny", age: 10)
let mike: PersonC = PersonC(name: "mike", age: 15, nickName: "m")

옵셔널 사용: 'PersonC' 클래스는 'nickName'을 옵셔널로 설정하여 초기값이 필요 없을 때 사용할 수 있습니다. 옵셔널을 사용하면 프로퍼티가 선택적이며, 초기화 시 값을 설정하지 않아도 됩니다. 'convenience' 이니셜라이저를 통해 추가적인 초기값을 설정할 수 있습니다.


🍏 암시적 추출 옵셔널과 초기화

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

let happy: Puppy = Puppy(name: "happy", owner: jenny)
happy.goOut() // happy가 주인 Jenny와 산책을 합니다.

암시적 추출 옵셔널: 'Puppy' 클래스는 'owner' 프로퍼티를 반드시 초기화해야 합니다. 'happy' 인스턴스를 생성할 때 'jenny'를 주인으로 설정하며, 이후 'goOut()' 메서드를 호출하여 주인과 함께 산책하는 기능을 제공합니다.


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

class PersonD {
    var name: String
    var age: Int
    var nickName: String?
    
    init?(name: String, age: Int) {
        if (0...120).contains(age) == false {
            return nil // 나이가 유효하지 않은 경우 nil 반환
        }
        
        if name.count == 0 {
            return nil // 이름이 비어있는 경우 nil 반환
        }
        
        self.name = name
        self.age = age
    }
}

let john: PersonD? = PersonD(name: "john", age: 23)
let joker: PersonD? = PersonD(name: "joker", age: 123)
let batman: PersonD? = PersonD(name: "", age: 10)

print(joker) // nil
print(batman) // nil

실패 가능한 이니셜라이저: 'PersonD' 클래스는 조건에 따라 인스턴스 생성에 실패할 수 있습니다. 나이가 유효하지 않거나 이름이 비어 있을 경우, 이니셜라이저는 'nil'을 반환합니다. 이러한 이니셜라이저는 옵셔널 타입으로 정의되어 실패 가능성을 나타냅니다.


🍏 디이니셜라이저

class PersonE {
    var name: String
    var pet: Puppy?
    var child: PersonC
    
    init(name: String, child: PersonC) {
        self.name = name
        self.child = child
    }
    
    deinit {
        if let petName = pet?.name {
            print("\(name)\(child.name)에게 \(petName)를 인도합니다.")
            self.pet?.owner = child
        }
    }
}

var donald: PersonE? = PersonE(name: "donald", child: jenny)
donald?.pet = happy
donald = nil
// donald 인스턴스가 더이상 필요없으므로 메모리에서 해제됩니다.
// donald가 jenny에게 happy를 인도합니다.

디이니셜라이저: 'deinit'은 'PersonE' 인스턴스가 메모리에서 해제될 때 호출됩니다. 이 시점에 'pet' 프로퍼티의 이름을 출력하고, 'pet'의 소유자를 'child'로 설정하여 메모리 해제 시 리소스를 적절히 정리합니다.


💡 코드

import UIKit

/* 인스턴스의 생성과 소멸 */

// 이니셜라이저와 디이니셜라이저
// init, deinit

// MARK: - 프로퍼티 기본값

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

class PersonA {
    var name: String = "unknown"  // 기본값 "unknown"을 가진 프로퍼티
    var age: Int = 0               // 기본값 0을 가진 프로퍼티
    var nickName: String = "geuni" // 기본값 "geuni"를 가진 프로퍼티
}

let jason: PersonA = PersonA() // PersonA 인스턴스 생성
jason.name = "jason"       // name 프로퍼티를 "jason"으로 설정
jason.age = 30            // age 프로퍼티를 30으로 설정
jason.nickName = "j"      // nickName 프로퍼티를 "j"로 설정

// MARK: - 이니셜라이저

// 프로퍼티 기본값을 지정하기 어려운 경우에는
// 이니셜라이저를 통해
// 인스턴스가 가져야 할 초기값을 전달할 수 있습니다.

class PersonB {
    var name: String
    var age: Int
    var nickName: String
    
    // 이니셜라이저
    // 인스턴스 생성 시 name, age, nickName 값을 설정합니다.
    init(name: String, age: Int, nickName: String) {
        self.name = name
        self.age = age
        self.nickName = nickName
    }
}

let hana: PersonB = PersonB(name: "hana", age: 20, nickName: "하나")
// PersonB 인스턴스 생성 시 이니셜라이저를 통해 프로퍼티 값을 설정

// MARK: 프로퍼티의 초기값이 꼭 필요 없을 때 (옵셔널 사용)

// 옵셔널 프로퍼티를 사용하면 프로퍼티의 초기값 설정이 필수가 아닙니다.
// 옵셔널은 값이 없을 수도 있음을 나타내며, 초기화 시 값을 설정하지 않아도 됩니다.

class PersonC {
    var name: String
    var age: Int
    var nickName: String? // 옵셔널 프로퍼티: 초기값을 설정하지 않아도 됩니다
    
    // 편의 이니셜라이저
    // 추가적인 초기값을 설정할 수 있는 이니셜라이저
    convenience init(name: String, age: Int, nickName: String) {
        self.init(name: name, age: age) // 기본 이니셜라이저 호출
        self.nickName = nickName        // 추가적으로 nickName 초기화
    }
    
    // 기본 이니셜라이저
    // 필수 프로퍼티만 초기화
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

let jenny: PersonC = PersonC(name: "jenny", age: 10) // nickName은 초기화되지 않음
let mike: PersonC = PersonC(name: "mike", age: 15, nickName: "m") // nickName도 초기화됨

// MARK: 암시적 추출 옵셔널과 초기화

// 암시적 추출 옵셔널을 사용하면 값이 항상 존재한다고 가정할 수 있습니다.
// 따라서 인스턴스 생성 시 반드시 값을 제공해야 합니다.

class Puppy {
    var name: String
    var owner: PersonC
    
    init(name: String, owner: PersonC) {
        self.name = name
        self.owner = owner
    }
    
    func goOut() {
        // 산책하는 메시지를 출력합니다.
        print("\(name)가 주인 \(owner.name)와 산책을 합니다.")
    }
}

let happy: Puppy = Puppy(name: "happy", owner: jenny) // Puppy 인스턴스 생성
happy.goOut() // happy가 주인 Jenny와 산책을 합니다.

// MARK: 실패 가능한 이니셜라이저

// 실패 가능한 이니셜라이저는 인스턴스 생성에 실패할 수 있음을 나타냅니다.
// 조건을 만족하지 않으면 nil을 반환하며, 이니셜라이저의 반환 타입은 옵셔널입니다.

class PersonD {
    var name: String
    var age: Int
    var nickName: String?
    
    init?(name: String, age: Int) {
        // 나이가 유효한 범위(0~120)인지 확인
        if (0...120).contains(age) == false {
            return nil // 나이가 유효하지 않은 경우 nil 반환
        }
        
        // 이름이 비어있는지 확인
        if name.count == 0 {
            return nil // 이름이 비어있는 경우 nil 반환
        }
        
        self.name = name
        self.age = age
    }
}

let john: PersonD? = PersonD(name: "john", age: 23)
let joker: PersonD? = PersonD(name: "joker", age: 123) // 나이가 유효하지 않음
let batman: PersonD? = PersonD(name: "", age: 10) // 이름이 비어있음

print(joker) // nil
print(batman) // nil

// MARK: - 디이니셜라이저

// 디이니셜라이저는 인스턴스가 메모리에서 해제될 때 호출됩니다.
// 리소스 정리 및 상태 설정을 할 수 있습니다.

class PersonE {
    var name: String
    var pet: Puppy?
    var child: PersonC
    
    init(name: String, child: PersonC) {
        self.name = name
        self.child = child
    }
    
    // 디이니셜라이저
    // 인스턴스가 메모리에서 해제될 때 호출
    deinit {
        if let petName = pet?.name {
            // pet 프로퍼티가 설정되어 있는 경우, 해당 pet의 이름을 출력
            print("\(name)\(child.name)에게 \(petName)를 인도합니다.")
            self.pet?.owner = child // pet의 소유자를 child로 설정
        }
    }
}

var donald: PersonE? = PersonE(name: "donald", child: jenny)
donald?.pet = happy
donald = nil
// donald 인스턴스가 더 이상 필요없으므로 메모리에서 해제됩니다.
// donald가 jenny에게 happy를 인도합니다.
profile
꾸준히 기록하기

0개의 댓글