swift 기본문법 - 인스턴스의 생성과 소멸 (init, deinit)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
인프런, 야곰의 스위프트 기본문법 강좌를 듣고 정리하였습니다.


인스턴스의 생성과 소멸 (init, deinit)

이전까지 클래스, 구조체를 공부했을때는 저장 프로퍼티의 값을 모두 할당해줬었다.

class PersonA {
  var name: String = "unknown"
  var age: Int = 0
  var nickName: String = "nick"
}

let zehye: PersonA = PersonA()

그 이유는 인스턴스가 생성되었을 때 해당 프로퍼티는 정상적인 값들로 초기화 되어야한다는 규칙이 있다.
그래서 기본값을 할당해놓지 않는다면 오류가 발생하게 된다. > 인스턴스가 됐을때 정상적으로 값이 들어있지 않다.

저장 프로퍼티의 초기값이 없다.

지금까지는 인스턴스를 생성한 다음에 프로퍼티에 적절한 값들을 넣어줬는데, 이는 의도와 다를 수도 있다.
초기화와 동시에 프로퍼티에 값을 할당하고 싶은데 그런 방법이 없었다.

그래서 init을 사용하면 프로퍼티 기본값을 사용하지 않더라도 실제로 인스턴스가 초기화될때 원하는 값들을 할당해줄 수 있다.

즉, 프로퍼티 기본값을 지정하기 어려울때 이니셜라이저를 통해 인스턴스가 가져야 할 초기값을 전달할 수 있다.

init(이니셜라이저)

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 zehye: PersonB = PersonB(name: "zehye", age: 20, nickName: "지혜")

그런데 꼭 프로퍼티의 초기값이 필요하지 않을때도 있다. 그럴때 옵셔널을 사용한다!

class PersonC {
  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
  }

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }
}

그런데 이렇게 되면 init이 너무 많아지게 된다. 중복되는 코드도 늘어나고…
이 경우에는 아래에 미리 만들어진 init을 사용하면된다. 이때 자신의 init을 사용할때 convenience를 init 앞에 붙여주면 된다.

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
  }
}

// 아래와 같이 init을 다양한 모습으로 사용이 가능하다
let h: PersonC = PersonC(name: "h", age: 10)
let s: PersonC = PersonC(name: "s", age: 20, nickName: "에스")

암시적 추출 옵셔널(!)

암시적 추출 옵셔널은 인스턴스 사용에 꼭 필요하지만, 초기값을 할당하지 않고자 할 때 사용한다.

class Puppy {
  var name: String
  var owner: PersonC!

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

즉, 프로퍼티가 꼭 필요한 경우에 느낌표를 찍어주는데 초기화할때 전달되기 어려운 값들의 경우

  1. name만 먼저 받아놓고
  2. 나중에 owner을 셋팅해주겠다는 의미

즉, owner값이 들어오지 않으면 런타임 오류가 발생한다. 주인을 할당해준 이후에 동작이 가능하다.

실패가능한 이니셜라이저

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

class PersonD {
  var name: String
  var age: int
  var nickName: String?

  init?(name: String, age: Int) {
    if (0...120).contains(age) == false {
      return nil
    }
    if name.characters.count == 0 {
      return nil
    }
    self.name = name
    self.age = age
  }
}

let zehye: PersonD? = PersonD(name: "zehye", age= 20)

이때 옵셔널이 아닌 타입으로 인스턴스를 생성하려고 하면 오류를 보여준다. > let zehye: PersonD = PersonD(name: "zehye", age: 20)

실패가능한 이니셜라이저이기 때문에 반환이 옵셔널로 오는데, 옵션널이 아닌 타입으로 선언을 했기 때문이다.

deinit(디이니셜라이저)

deinit은 클래스의 인스턴스가 메모리에서 해제되는 시점에 호출된다.
즉, 인스턴스가 해제되는 시점에 해야할 일을 구현할 수 있음을 의미한다. > 클래스 타입에만 구현해줄 수 있다.

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: "h")
donald.pet = happy
donald = nil  // donald가 메모리에서 해제될 시점에 deinit을 실행한다.