Diana의 iOS 개발일기

[스위프트 프로그래밍 3판] - 11. 인스턴스 생성 및 소멸 본문

Swift/책 정리

[스위프트 프로그래밍 3판] - 11. 인스턴스 생성 및 소멸

Diana_iOS 2021. 3. 9. 11:08

인스턴스는 메모리와 직접적으로 연관이 있다보니 수 많은 언어들에서 중요하게 다뤄집니다.

스위프트 또한 메서드, 프로퍼티 등을 사용하기 위해서는 인스턴스 생성이 거의 필수로 이루어지고 메모리의 낭비를 막기 위해서는 생성된 인스턴스를 알맞게 소멸시켜줄 수 있어야 합니다.

이번 내용은 이니셜라이저를 통한 인스턴스 생성, 초기화 등을 통한 인스턴스을 다루고 있습니다.


[이니셜라이저(Initializer)]

1. 초기화(initialize)

구조체, 클래스 그리고 열거형 등은 생성한 뒤 인스턴스를 사용하기 위해서는 프로퍼티 값들을 초기화 해주어야 합니다.

 

초기화 방법에는 두가지가 있는데 하나는 저장 프로퍼티 생성과 동시에 초기화를 해주는 방법이고 다른 하나는 아래와 같이 init 키워드를 사용하여 이니셜라이저를 생성 한 뒤 그 안에서 초기화를 해주는 방법이 있습니다.

 

참고로 구조체는 생성 시 자동으로 모든 프로퍼티를 포함한 이니셜라이저인 멤버와이즈 이니셜라이저가 생성됩니다.

이는 클래스에서는 지원하지 않으므로 구분하여 알아둘 필요가 있습니다.

//클래스
class SomeClass { 
    init(){//클래스의 경우 생성 후 이니셜라이저를 통해 초기값을 지정해주어야 인스턴스 사용이 가능하다
        //초기화를 위한 코드(이니셜라이저)
    }
}

//구조체
struct SomeStruct {
    init(){//구조체는 생성시 이니셜라이저가 자동으로 생성된다
        //초기화를 위한 코드
    }
}

//열거형
enum SomeEnum { 
    case someCase
    
    init(){
        //열거형은 초기화할 때 반드시 case중 하나가 되어야 한다
    }
}

 

2. 매개변수(Parameter)

이니셜라이저 또한 메서드의 한 종류로 볼 수 있으므로 매개변수를 가진 사용자 정의 이니셜라이저를 생성할 수 있습니다.

struct Area{
    var squareMeter: Double
    
    init(fromPy py: Double) { //사용자 정의 이니셜라이저는 한개 이상 설정 가능합니다
        squareMeter = py * 3.3058
    }
    
    init(fromSquareMeter squareMeter:Double) {
        self.squareMeter = squareMeter
    }
    
    init(value: Double) {
        squareMeter = value
    }
    
    init(_ value: Double) {
        squareMeter = value
    }
}

let roomOne: Area = Area(fromePy: 15.0)
print(roomOne.squareMeter) //49.587

let roomTwo: Area = Area(fromSquareMeter: 33.06)
print(roomTwo.squareMeter) //33.06

let roomThree: Area = Area(value: 30.0)
let roomFour: Area = Area(55.0)

 

 

사용자 정의 이니셜라이저를 생성할 경우 기본 이니셜라이저(init())는 별도로 구현해야 사용할 수 있으며 이니셜 라이저 안에 또다른 이니셜라이저를 사용함으로써 초기화를 위임할 수 도 있습니다.

enum Student {
    case elementary, middle, high
    case none
    
    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

 

참고로 초기화는 옵셔널 프로퍼티가 아닌 이상 무조건 이루어져야 한다 는 조건을 가지고 있으며 반대로 옵셔널 프로퍼티의 경우 초기화를 하지 않아도 사용이 가능합니다.

한 구조체나 클래스 안에 옵셔널이 지정된 프로퍼티와 그렇지 않은 프로퍼티가 동시에 존재한다면 옵셔널이 지정되지 않은 프로퍼티만 초기화 하는 것도 가능합니다.

class Person {
    var name: String
    var age: Int?
    
    init(name: String){//옵셔널로 지정되지 않은 name만 초기화 합니다
        self.name = name
    }
}

let myName: Person = Person(name: "test")
print(myName.age) //초기화 되지 않은 옵셔널 프로퍼티를 출력하므로 결과는 nil 입니다

 

 

3. 실패 가능한 이니셜라이저(Failable initializer)

이니셜라이저를 사용할 때 생기는 여러 예외 상황 중 하나는 이니셜라이저의 전달인자로 잘못된 값이 들어가는 것 입니다. 이 경우 인스턴스는 초기화에 실패하여 에러를 띄우게 됩니다.

이러한 예외상황들을 고려하여 init키워드 뒤에 "?"를 붙여 실패 가능성을 열어두는 것을 실패 가능한 이니셜라이저 라고 합니다.

결국 nil을 염두해 놓은, 옵셔널이 떠오르는 기능임을 알 수 있습니다.

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) //20이상은 case에 없네요
print(younger) //전달인자가 잘못되었어! nil을 반환합니다

younger = Student(bornAt: 2020, currentYear: 2016) //2020년에 태어나 현재 2016년이다? 이상하네요
print(younger) //nil

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

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

[디이니셜라이저(Deinitializer)]

클래스 인스턴스에서는 이니셜라이저를 통해 메모리를 할당해서 사용을 한 뒤에는 할당되었던 메모리를 해제해 주어야 메모리 낭비를 막을 수 있습니다.

이 역할을 해주는 것이 바로 디이니셜라이저로, 디이니셜라이저는 클래스 인스턴스에서만 사용되지만 모든 클래스 인스턴스가 아닌 외부 자원을 인스턴스 내부에 가져와 사용한 인스턴스에 한에 유용하게 사용됩니다.

class SomeClass {
    deinit {//해당 클래스 인스턴스는 메모리 해제 전 아래 구문을 출력한다
        print("Instance will be deallocated immediately")
    }
}

var instance: SomeClass? = SomeClass()
instance = nil

 

해당 글은 야곰님의 스위프트 프로그래밍 3판 을 기반으로 한 정리 글이며 문제가 있을 시 삭제하도록 하겠습니다.

스위프트 프로그래밍 3판 eBook 구매 링크: www.yes24.com/Product/Goods/81530016

 

스위프트 프로그래밍 (3판)

문법을 넘어 프로그래밍 패러다임도 익히는 스위프트 5스위프트 5의 핵심 키워드는 ‘안정화’다. ABI 안정화 덕분에 버전과 환경에 크게 영향받지 않고 더 유연하게 스위프트를 사용할 수 있게

www.yes24.com

야곰님의 블로그: blog.yagom.net/

 

yagom's blog

야곰의 프로그래밍 블로그입니다. iOS, Swift, Objective-C, C에 대해 이야기합니다.

blog.yagom.net