인스턴스 생성 및 소멸

- 클래스 인스턴스의 소멸 시점 : ARC에 의해 결정된다

 

  • 이니셜라이저
    • 저장 프로퍼티의 초깃값 설정
    • 옵셔널 프로퍼티는 제외
  • 프로퍼티의 기본값
    • var num = 100
  • 상수 프로퍼티의 초기화
    • lazy var 얘기할 때 한번 언급한 것 같은데
    • let(상수) 프로퍼티는 초기화 과정에서만 값을 할당할 수 있다
  • 실패 가능한 이니셜라이저
    • init?(...)
    • 대표적으로 이니셜라이저의 전달인자로 잘못된 값이 전달되었을 때, 초기화 실패할 수 있다
    • 실패를 염두한 이니셜라이저
  • 함수를 사용한 프로퍼티 기본값 설정
    • iOS에서 UI 컴포넌트를 클래스의 프로퍼티로 구현하고, UI 컴포넌트의 생성과 동시에 Constraint등의 설정할 때 용이함
  • 인스턴스 소멸
    • deinit은 클래스에서만 구현이 가능함

이번달 내로 2020년 회고 쓸건데 이거 언제 다하고 쓰지...

 

프로퍼티와 메서드

  • 프로퍼티
    • 저장 프로퍼티
      • 변수 or 상수
      • 클래스와 구조체에 포함
    • 연산 프로퍼티
      • 연산을 실행한 결괏값
      • 실제로 데이터 값을 갖지 않음
      • 클래스, 구조체, 열거형에 포함
      • getter/setter가 존재
      • 왜 사용하는가?
        • 메서드로 구현시, get/set Method 2개를 구현해야 하는데, 보다 훨씬 간편함
    • 타입 프로퍼티
      • 특정 타입에 사용
      • static 키워드 사용
    • 지연 저장 프로퍼티
      • 처음으로 호출될 때, 초기화를 진행한다 
      • lazy var 키워드 사용
      • lazy let은 사용할 수 없다 -> (let은 생성될 때, 초기화된 후로 변경될 수 없기 때문이 아닐까?)
      • 여러 쓰레드가 동시에 접근 시, 여러번 초기화 가능성이 있다. -> Non-Thread-Safe
//연산 프로퍼티
struct Point{
	var x: Float
    var y: Float
	var oppositePoint: Self{
    	get{
        	return Point(x: -x, y: -y)
        }
        //set은 생략가능 -> 읽기전용 프로퍼티로 선언/사용 가능
        set(opposite){
        	x = -opposite.x
            y = -opposite.y
        }
    }
}

 

프로퍼티 감시자

- 값이 새로 할당될 때마다 호출

- 저장/연산 프로퍼티에 사용 가능

- willset/didset 구현

- 현재 값과 같더라도 실행된다

- 오버라이드가 가능하다

class Account{
	var credit: Int = 0{
    	willSet{
        	print("잔액이 \(credit)에서 \(newValue)로 변경될 것입니다")
        }
        didSet{
        	print("잔액이 \(oldValue)에서 \(credit)으로 변경되었습니다")
        }
    }
}

 

타입 프로퍼티

- 인스턴스가 아닌 각각의 타입 다체에 속하는 프로퍼티

- static let, static var 사용 가능하지만, 연산 프로퍼티는 var로만 선언 가능

- 반드시 초깃값을 설정해야 하며, 지연 연산된다

- 지연 저장 프로퍼티(lazy var)와는 다르게 다중 쓰레드 환경에서도 단 한번만 초기화 된다


메서드

  • 인스턴스 메서드
    • 인스턴스가 존재할 때만 사용 가능
    • 구조체/열거형에서 인스턴스 내부 값(프로퍼티)을 변경하기 위해서는 mutating func로 선언
  • 타입 메서드
    • static func / class func 가 존재한다
      • 둘다 같은 타입 메서드지만, static은 상속 불가, class는 상속 가능 타입 메서드이다
    • 여기서의 Self는 인스턴스가 아닌 타입 자신을 의미한다

 

구조체와 클래스 (struct, class)

 

  • 구조체(struct)
    • 값 타입(value type) - 값이 복사됨
    • 상속 불가능
    • deinit 없음
    • 참조카운팅 없음
    • Swift의 대부분의 데이터 타입은 구조체로 작성되어 있다
  • 클래스(class)
    • 참조 타입(reference type) - 인스턴스를 참조하여 공유함
    • 상속 가능
    • deinit 있음
    • 참조 카운팅 있음

가장 큰 차이점은 값/참조 타입이다. -> 이에 대한 자세한 내용은 후에 나온다.

B.U.T. 구글링해서 좀 알아보고 읽으면 이해가 더! 잘될 것이라 생각한다.

 

클래스 vs 구조체

struct PersonStruct{
	var name: String
}

class PersonClass{
	var name: String
    
    deinit{
    	print("소멸됐어염")
    }
    
}

let structPerson = PersonStruct(name: "tree")
let classPerson = PersonClass(name: "tree")
var classPersonVar = PersonClass(name: "tree")

structPerson.name = "randy" // 변경 불가, 값 타입을 상수로 선언 시, 내부 프로퍼티 또한 변경할 수 없다
classPerson.name = "randy" // 변경 가능, 참조 타입을 상수로 선언 시, 참조하는 포인터(?)를 가진 classPerson만 변경이 불가

classPersonVar.name = "randy" // 가능
classPersonVar = nil // 소멸됐어염

 

간단하게만 차이를 알아보자

  구조체 클래스
메모리 영역 Stack Heap
속도 빠름 느림
상속 불가능 가능

 

선택하기

  • 애플은 가이드 라인에서 다음 조건 중 하나 이상에 해당한다면 구조체를 사용하라고 권장한다
    • 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
    • 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
    • 구조체에 저장된 프로퍼티가 값 타입이며, 참조하는 것보다 복사하는 것이 합당할 때
    • 상속받거나 상속할 필요가 없을 때

스위프트의 데이터 타입들이 대부분 구조체라서 속도가 빠른 건 알겠어!

B.U.T. 쓸데없이 메모리를 많이 잡아 먹는 것 아냐? 매개변수로든 치환이든 다 복사해서 메모리에 올라가잖아?

-> 스위프트는 꼭 필요한 경우에만 "진짜 복사"를 한다고 한다.

-> 진짜 복사 : 메모리에 실제로 데이터를 복사하여 올리는 것

-> 스위프트가 적절히 효율적으로 처리한다고 하는데 그에 대한 기준은 모르겠다... 나중에 시간나면 찾아보는 걸로...

-> 혹여나 이걸 보는 사람은 없겠지만, 혹시라도 보다가 궁금해서 찾게되면 저도 알려주세요..bb

 

 

 

하... 이제 옵셔널이다

 

옵셔널

- 사실 옵셔널에는 내용이 굉장히 적다.

- B.U.T. 어느 문법이던 중요하겠지만 Swift에서도 굉장히 중요한 표현이다

- 안드로이드 개발을 진행할 때는, 옵셔널이라는 명칭보다는 nullable이라는 단어를 사용했다.

- 스위프트의 특징인 안전성을 담보로 사용할 수 있는 기능이다

- 옵셔널과 옵셔널이 아닌 값은 철저히 다른 타입으로 인식하기 때문에, 컴파일 시점에서 오류를 걸러낸다.

 

그렇다면 왜 옵셔널을 사용하는가?

  • 함수의 전달인자의 값이 잘못된 값일 때
  • 매개변수를 굳이 넘기지 않아도 될 때
  • 열거형을 불러올 때, case에 존재하지 않을 때

앞에서 enum에서 알아봤 듯이, 옵셔널은 열거형이다

 

옵셔널 추출

  • 옵셔널 바인딩
    • if 사용 - if블록 내에서만 사용 가능
    • guard 사용 - 블록외의 블록에서 사용 가능
  • 암시적 추출
    • ! 사용 - 위험한 방법
struct Person{
	var name: String?
}

var person: Person = Person(name: "tree")

//if 사용
if let name = person.name{
	print(name) // tree
}

//guard 사용
guard let name = person.name else { ... }

print(name) // tree

//암시적 추출 사용(1)
let name = person.name!
print(name) // tree

var person2 = Person()
let name2 = person2.name! // 런타임 에러 발생

 

+ Recent posts