개발하는 뚝딱이

[swift] 인스턴스 생성 및 소멸 본문

swift

[swift] 인스턴스 생성 및 소멸

개발자뚝딱이 2020. 5. 1. 01:03

 

책 <스위프트 프로그래밍 3판 ;야곰 지음> 을 정리한 글입니다.

 


1.0 인스턴스 생성

- 이니셜라이저를 통해 해당 타입의 새로운 인스턴스를 생성

- init 메서드는 struct, enum의 구현부와 extension에서 구현 가능하지만 class는 구현부에서만 가능

class SomeClass{
    init() {
        // ...
    }
}

struct SomeStruct {
    init() {
        // ...
    }
}

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

 

1.1 프로퍼티 기본값

- 구조체와 클래스의 인스턴스를 처음 생성할 때, 옵셔널 저장 프로퍼티를 제외한 모든 저장 프로퍼티에 초깃값 (initial value)을 할당해야 함

- 프로퍼티 기본값이 있으면 초깃값을 할당하지 않아도 초기화 가능

- 이니셜라이저를 통해 초깃값이 할당될 때 프로퍼티 감시자 메소드는 호출되지 않음

struct Area {
    var squareMeter: Double
    
    init() {
        squareMeter = 0.0 // initial value 할당
    }
}

let room: Area = Area()
print(room.squareMeter) // 0.0


struct Area {
    var squareMeter: Double = 0.0 // 프로퍼티 기본값 할당
}

let room: Area = Area()
print(room.squareMeter) // 0.0

 

1.2 이니셜라이저 매개변수

- 사용자 정의 이니셜라이저를 만들면 기존의 기본 이니셜라이저, 멤버와이즈 이니셜라이저 사용 불가

- 사용자 정의 이니셜라이저를 정의하고, 기본 이니셜라이저나 멤버와이즈 이니셜라이저를 사용하고 싶으면 익스텐션으로 사용자 정의 이니셜라이저를 구현하면 됨

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(fromPy: 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)

Area() // 에러

 

1.3 옵셔널 프로퍼티 타입

- 초기화 과정에서 값을 초기화하지 않아도 되는 저장 프로퍼티의 경우 옵셔널로 선언

- 초기화 과정에서 값을 지정해주기 어려운 저장 프로퍼티의 경우 옵셔널로 선언

- 옵셔널로 선언한 저장 프로퍼티는 초기화 과정에서 값을 할당해주지 않으면 nil이 자동 할당

class Person {
    var name: String?
    var age: Int?
    
    init(name: String) {
        self.name = name
    }
}

let jin: Person = Person(name: "Jin")
print(jin.name) // "jin"
print(jin.age) // nil

jin.age = 99
print(jin.age) // 99

 

1.4 상수 프로퍼티

- let으로 선언한 저장 프로퍼티

- 인스턴스 초기화 과정에서만 값 할당 가능, 이후에는 변경 불가능

- 클래스 인스턴스의 상수 프로퍼티는 프로퍼티가 정의된 클래스에서만 초기화 가능

- 해당 클래스를 상속받은 자식클래스의 이니셜라이저에서는 부모클래스의 상수 프로퍼티 값을 초기화 할 수 없음

 

 

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

- 기본 이니셜라이저 (init메소드)

- 멤버와이즈 이니셜라이저 (타입 이름을 사용한 이니셜라이저 )

- 사용자 정의 이니셜라이저를 정의하지 않으면 클래스나 구조체는 모든 프로퍼티에 기본값이 지정되어 있다는 전제하에 기본 이니셜라이저를 사용

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

- 구조체는 사용자 정의 이니셜라이저인 멤버와이즈 이니셜라이저를 기본으로 제공

- 클래스는 멤버와이즈 이니셜라이저를 지원하지 않음

struct Point {
    var x: Double = 0.0
    var y: Double = 0.0
}

struct Size {
    var width: Double = 0.0
    var height: Double = 0.0
}

let point: Point = Point(x: 0, y: 0)
let size: Size = Size(width: 50.0, height: 50.0)

// 구조체의 저장 프로퍼티에 기본값이 있는 경우
// 필요한 매개변수만 사용하여 초기화
let somePoint: Point = Point()
let someSize: Size = Size(width: 50)
let anotherPoint: Point = Point(y: 100)

 

1.6 초기화 위임

- 값 타입인 구조체와 열거형은 코드의 중복을 피하기 위해 이니셜라이저가 다른 이니셜라이저에게 일부 초기화를 간단하게 위임 가능함

- 값 타입에서 이니셜라이저가 다른 이니셜라이저를 호출하려면 self.init 사용 (이니셜라이저 안에서만 사용)

- 클래스는 상속을 지원하므로 간단한 초기화 위임 불가능 (하지만 가능하다)

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: 2002, currentYear: 2020)
print(younger) // high

 

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

- Failable initializer

- 클래스, 구조체, 열거형 등에 모두 정의할 수 있음

- 실패했을 때 nil 반환, 반환타입이 옵셔널 / 성공했을 때는 값을 반환하지 않음

- init 대신에 init? 사용

- 실패하지 않는 이니셜라이저와 실패 가능한 이니셜라이저를 같은 이름과 같은 매개변수 타입을 갖도록 정의할 수 없음

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 sue: Person? = Person(name: "Sue", age: 99)

if let person: Person = sue {
    print(person.name)
} else {
    print("Person wasn't initialized")
}
// Sue


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

 

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

- 사용자 정의 연산을 통해 저장 프로퍼티 기본값을 설정할 때, 클로저나 함수 사용 가능

- 인스턴스 초기화할 때 함수나 클로저가 호출되면서 연산 결과값이 프로퍼티 기본값으로 제공

- 클로저나 함수의 반환 타입은 프로퍼티의 타입과 일치해야 함

 

- 클로저 사용 시, 클로저 내부에서 인스턴스의 다른 프로퍼티를 사용하여 연산할 수 없음 (다른 프로퍼티 값이 설정되기 전)

- 클로저 내부에서 self 프로퍼티 사용할 수 없으며 인스턴스 메서드 호출도 할 수 없음

// 클로저를 통한 프로퍼티 기본값 설정

class SomeClass {
    let someProperty: SomeType = {
        // 새로운 인스턴스 생성하고 사용자 정의 연산을 통한 후 반환
        // 반환되는 값의 타입은 SomeType과 같아야 함
        return someValue
    }()
}
// 클로저를 통한 student 프로퍼티 기본값 설정

struct Student {
    var name: String?
    var number: Int?
}

class SchoolClass {
    var students: [Student] = {
        // 새로운 인스턴스를 생성하고 사용자 정의 연산을 통한 후 반환
        // 반환되는 값의 타입은 [Student] 타입이어야 함
        
        var arr: [Student] = [Student]()
        
        for num in 1...15 {
            var student: Student = Student(name: nil, number: num)
            arr.append(student)
        }
        
        return arr
    }()
}

let myClass: SchoolClass = SchoolClass()
print(myClass.students.count) // 15

 

2. 인스턴스 소멸

- 클래스의 인스턴스는 디이니셜라이저 Deinitializer를 구현

- 디이니셜라이저는 메모리에서 해제되기 직전 클래스 인스턴스와 관련하여 원하는 정리작업을 구현

- 클래스의 인스턴스가 메모리에서 소멸되기 바로 직전에 호출

- 디이니셜라이저는 클래스의 인스턴스에서만 구현 가능

 

- 스위프트는 인스턴스가 더 이상 필요하지 않으면 자동으로 메모리에서 소멸시킴

- 소멸시킬 때 디이니셜라이저를 사용해 별도의 메모리 관리 작업을 할 필요는 없지만, 외부 자원을 사용하는 등의 경우에 필요함

- 하나만 구현가능하며 매개변수가 없고 자동호출되므로 별도의 코드로 호출 불가

// 디이니셜라이저의 구현

class SomeClass {
    deinit {
        print("Instance will be deallocated immediately")
    }
}

var instance: SomeClass? = SomeClass()
instance = nil // Instance will be deallocated immediately
// FileManager 클래스의 디이니셜라이저 활용

class FileManager {
    var fileName: String
    
    init(fileName: String) {
        self.fileName = fileName
    }
    
    func openFile() {
        print("Open File: \(self.fileName)")
    }
    
    func modifyFile() {
        print("Modify File: \(self.fileName)")
    }
    
    func writeFile() {
        print("Write File: \(self.fileName)")
    }
    
    func closeFile() {
        print("Close File: \(self.fileName)")
    }
    
    deinit {
        print("Deinit instance")
        self.writeFile()
        self.closeFile()
    }
}

var fileManager: FileManager? = FileManager(fileName: "abc.txt")

if let manager: FileManager = fileManager {
    manager.openFile() // Open file: abc.txt
    manager.modifyFile() // Modify File: abc.txt
    manager.closeFile() // Close File: abc.txt
}

fileManager = nil
// Deinit instance
// Write File: abc.txt
// Close File: abc.txt

'swift' 카테고리의 다른 글

[swift] 클로저  (0) 2020.05.07
[swift] 접근제어  (0) 2020.05.02
[swift] 옵셔널  (0) 2020.04.28
[swift] 메소드 (method)  (0) 2020.04.27
[swift] 프로퍼티(property)  (0) 2020.04.15