IOS/Swift

[TIL] Swift. Class 1편

1. 기본 개념

  • Class란 구조체(struct)와 비슷한 것이다
  • 관계가 있는 것들을 묶어서 표현
  • Object = Data + Method
    • Object → Structure와 Class를 이용해 구현
    • Data → Property
    • Method → Method
  • Structure 메모리 관리 타입 -> Stack
    • Fast - 당장실행 해야하거나, 컨트롤, 매니징 해야하는 것들
      • 함수에서 변수를 만들면 스택에 생성되고 계산이 끝나고 리턴하게 되면 스택에서 제거
  • Class 메모리 관리 타입 → Heap
    • Slow - Class같은 ref데이터를 저장하는데 사용합니다
    • Heap은 스택처럼 자동으로 데이터를 저장하지 않기 때문에 신경을 많이 써주어야한다.
  • 변수선언시 힙과 스택의 변화
    • 클래스 선언 → Heap 에 저장
    • 변수 선언 → 스택에 저장
    • 변수의 데이터가 class면 ref타입 이므로 Heap 에 저장된 데이터를 주소로 가짐(stack에서)

2. 클래스 인스턴스의 생성과 소멸

  • Stored Property에 값을 저장할때에는 기본값을 지정해 주어야 한다.
  • 스위프트의 모든 인스턴스는 초기화와 동시에 모든 프로퍼티에 유효한 값이 할당되어 있어야 합니다.
    프로퍼티에 미리 기본값을 할당해두면 인스턴스가 생성됨과 동시에 초기값을 지니게 됩니다.
  • 하지만 이런 초기값을 나중에 상황에 따라 만들고 싶다면 키워드 init을 활용하게 됩니다.
    • 1. Initializer
      • designated( 메인 초기값) and convenience( 보조 초기값 ) initialization의 규칙
        1. DI는 자신의 부모의 DI를 호출해야 한다
        2. CI는 같은 클래스의 이니셜라이저를 꼭 하나 호출해야한다
        3. CI는 궁극적으로는 DI를 호출해야 한다
        4. 모든 파라미터의 초기값을 세팅하지 않을때 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 (클래스 생성시 지켜야할 두가지 단계)
      1. 최하위 상속클래스의 store property의 초기값을 세팅하고
      2. 상속받은 클래스의 값을 세팅하기
      • 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를 인도합니다

'IOS > Swift' 카테고리의 다른 글

[TIL] Swift. dequeueReusableCell  (0) 2020.10.09
[TIL] Swift. Class 2편  (0) 2020.10.07
[TIL] Swift. Method  (0) 2020.10.06
[TIL] Swift. Property  (0) 2020.10.01
[TIL] Swift. Structure(Class, Struct, Enum)  (0) 2020.09.30