변수의 데이터가 class면 ref타입 이므로 Heap 에 저장된 데이터를 주소로 가짐(stack에서)
2. 클래스 인스턴스의 생성과 소멸
Stored Property에 값을 저장할때에는 기본값을 지정해 주어야 한다.
스위프트의 모든 인스턴스는 초기화와 동시에 모든 프로퍼티에 유효한 값이 할당되어 있어야 합니다. 프로퍼티에 미리 기본값을 할당해두면 인스턴스가 생성됨과 동시에 초기값을 지니게 됩니다.
하지만 이런 초기값을 나중에 상황에 따라 만들고 싶다면 키워드 init을 활용하게 됩니다.
1. Initializer
designated( 메인 초기값) and convenience( 보조 초기값 ) initialization의 규칙
DI는 자신의 부모의 DI를 호출해야 한다
CI는 같은 클래스의 이니셜라이저를 꼭 하나 호출해야한다
CI는 궁극적으로는 DI를 호출해야 한다
모든 파라미터의 초기값을 세팅하지 않을때 convenience init 을 쓸수 있다
class Person {
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 hyeongyu: Person = Person(name: "hyeongyu", age: 29, nickName: "현규")
1. Optional Initializer
만약 클래스안에 있는 프로퍼티가 있을지 없을지 확신할수 없는 경우 Optional을 사용할수 있다
하지만 이렇게 옵셔널을 사용할 경우 initalizer를 두번사용하여 옵셔널 값이 있는 경우 없는경우를 둘다 작성해 주어야한다.
override init은 먼저 세팅된 초기값을 변경할때 사용할수 있다.
상속받는 부모 클래스의 init은 super.init 키워드로 사용할 수 있다
상속받은 부모 클래스의 super.init을 사용해야 할때 주의 할점은 먼저 상속된 클래스의 stored property의 값들을 먼저 할당해 주어야 한다는 것이다
two phase initializations (클래스 생성시 지켜야할 두가지 단계)
최하위 상속클래스의 store property의 초기값을 세팅하고
상속받은 클래스의 값을 세팅하기
Phase 1 - All Stored Property have to be initialized = 이 페이즈 1이 끝나기 전에는 어떤 프로퍼티나 메서드를 사용할수 없습니다.
Phase 2 - Phase 1 의 반대방향으로 프로퍼티나 메서드를 가능하게 한다. = 따라서 먼저 스토어드 프로퍼티를 자식프로퍼티 → 부모프로퍼티 → 메서드작성 의 순서로 작성해야한다.
// optional initializer 사용시
class PersonC {
var name: String
var age: Int
var nickName: String? // var nickName: String? optional 사용
}
// 이니셜라이저
//convenience 키워드는 init의 내용이 겹칠때 이미 선언된 init을 사용할때 사용한다.
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
}
}
암시적 추출 옵셔널
인스턴스 사용에 꼭 필요하지만 초기값을 할당하지 않고자 할때 사용한다. ! 사용
class Puppy {
var name: String
var owner: PersonC!
init(name: String) {
self.name = name
}
func goOut() {
print("\(name)가 주인 \(owner.name)와 산책을 합니다")
}
}
//위의 클래스를 상속받아 변수를 선언합니다.
// 현재 오너가 없는 상태입니다. 하지만 꼭 필요합니다
let happy: Puppy = Puppy(name: "happy")
happy.goOut() //--> 아직 오너가 없기때문에 런타임 에러가 발생합니다. 현재오너값 nil
// 오너에 값을 넣어주기
happy.owner = jenny
happy.goOut() // 이때에는 happy가 주인 jenny와 산책을 합니다 가 출력됩니다.
실패 가능한 이니셜라이저
만약 매개변수로 전달되는 초기값이 잘못된 경우 인스턴스 생성에 실패하게 만드는 것 입니다.
생성에 실패하면 nil을 반환합니다
그래서 실패가능한 이니셜라이저의 반환타입은 옵셔널 타입입니다.
따라서 옵셔널 init을 사용하게 되면 반환값이 nil이 나올수 있기 때문에 클래스상속할때에 let John: PersonD? 라고해야 컴파일 에러가 나지 않습니다.
class PersonD {
var name: String
var age: Int
var nickName: String?
// if문안에 있는 조건을 만족하면 return nil로 인스턴스가 nil로 선언되어 반환됩니다.
init?(name: String, age: Int) {
if (0...120).contains(age) == false {
return nil
}
if name.count == 0 { // count가 0이라는 것은 "" 문자열이 없다는 것입니다
return nil
}
self.name = name
self.age = age
}
}
2. Deinitializer
deinit은 클래스가 인스턴스의 메모리에서 해제되는 시점에 호출됩니다
인스턴스가 해제되는 시점에 해야할 일을 구현할 수 있습니다.
해제되는 시점은 ARC(Automatic Reference Counting)에 의해서 정해집니다.
클래스의 연결이 단 하나라도 살아있는경우 해제되지 않습니다.
Deinitializer는 클래스 타입에서만 사용가능합니다.
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를 인도합니다