Swift 기본 정리 - ARC/Dispatch/Protocol/Generic

기술면접 대비 Swift 정리편
오니's avatar
Jun 22, 2024
Swift 기본 정리 - ARC/Dispatch/Protocol/Generic

ARC

Automatic Reference Count
  • 컴파일 타임에 동작
  • Strong, weak는 ARC테이블을 통해 간접참조
  • unowned은 ARC테이블을 사용하지 않고 직접 참조한다.
 

순환참조 찾는 방법

  1. Instrument - Leak
  1. Graph
  1. CFRetainCount로 디버깅
  1. 메모리 증감 살피기

autoReleasepool

- 해당 블록 내에서는 ARC 증감 내역을블록 종류 후에 반영한다.

Value Type | Refernece Type

왜 value type이 Reference type보다 빠를까?

  • Heap 메모리 선언시 오버헤드가 크기 때문이다.
    • Heap 메모리 저장하기 위한 Heap 메모리 탐색
    • Heap 저장한 메모리를 멀티 스레드에 sync 맞추기
    • 해제 과정도 멀티 스레드에 반영해야 함

Dynamic | Static Dispatch

Dispatch

  • 사전 뜻: 발송 → 어떤 함수를 실행 시킬 것인지 결정하는 것

class 내 Dispatch

  • 기본적으로 dynamic
  • private / final 키워드가 붙으면 static
    • private → 상속이 불가능
    • final → 상속이 불가능
  • extension 내 함수 → static
    • 상속 및 override 불가능 → Static

struct 내 Dispatch

  • static

protocol 내 Dispatch

  • 기본적으로 dynamic

protocol 선언 O / extension 구현 O / 객체 구현 O

Dynamic
  • 객체에 구현된 것이 우선적으로 적용됨으로 Dynamic

protocol 선언 O / extension 구현 O / 객체 구현 X

  • Static
    • extension에 구현되었고 상속이 불가능 함으로 Static

protocol 선언 X / extension 구현 O / 객체 구현 O

  • 상황에 따라 다르다.
    • protocol type의 객체 → Static (extension 사용)
    • protocol 상속받은 type → Dynamic (객체 사용)
    • protocol A { } extension A { func testFunc() { print("It's protocol") } } class B: A { func testFunc() { print("It's Object") } } let obj1: B = B() let obj2: A = B()
      obj1: B은 Dynamic obj2: A 는 Static으로 작동한다.

Swift의 Protocol은 어떻게 적용될까?

Protocol 배열

  • Protocol을 따르는 객체의 크기는 모두다 다른데 어떻게 메모리에 저장될까?
Existentical Container 활용

Existentical Container

notion image
  • Existentical Container는 위와 같이 구성됨

Value Buffer

  • Value Buffer에 실질적으로 Protocol을 따르는 객체의 메모리가 올라가게 됨
    • 객체의 크기가 3word 이상이다? → 객체는 Heap에 저장되고 Value Buffer에는 주소 값을 갖게 됨

Value Witness Table

  • 객체의 allocate deallocate copy destruct 를 맡음

Ptorocol Witness Table

  • protocl 함수 구현부 위치를 갖고 있는 테이블

Protocol Argument 함수

Protocol을 따르는 함수인자를 받을 때 어떻게 적용되나?
→ Existentical Container로 넘긴다.
→ 그럼 그게 class랑 뭐가 달라?
→ COW를 활용하여 최대한 heap을 덜 사용한다.

Swift의 Generic

protocol을 곁들인

// 1️⃣ struct A { init(a: Drawable, b: Drawable) } // 2️⃣ struct A<T: Drawable> { init(a: T, b: T) }
이 2개는 어떻게 다를까?

1️⃣ - 프로토콜 인자로 받음

이 경우 Extential Container로 Protocol객체를 받는다. value Buffer가 클 경우에 heap이 사용된다.

2️⃣ - Generic 인자로 받음

Swift Complier에서 해당 Generic을 사용해 만들 수 있는 모든 생성자를 만듬
protocol Pro { } class A: Pro { } struct B: Pro { } struct C<T: Pro> { init(a: T, b: T) }
위 예시인 경우 Swift Complier가 아래와 같이 생성자를 만든다.
struct C<T: Pro> { init(a: A, b: A) init(a: B, b: B) }
위와 같이 생성자를 직접 만듬으로 컴파일 시간에서 빌드되게 만들어준다.
→ 그럼 코드가 너무 많이 늘어나느거 아님?
사실상 최적화 때문에 사실상 오히려 코드가 줄어든다고 한다.
 
Share article

오니의 개발 블로그