Swift의 Dispatch

Swift의 Dispatch

~~ 런타임에 결정되는...

~~ 컴파일에 결정되는...

개발 문서를 보다보면 수 없이 마주치는 런타임과 컴파일!

런타임과 컴파일은 뭔지 알겠다. 그럼 그걸 결정하는 기준이 무엇일까?

Dispatch

  • Dispatch는 어떤 코드를 실행하는지 결정하는 메커니즘이다.

  • 즉, 런타임과 컴파일을 결정하는 메커니즘이 Dispatch이다.

  1. Static Dispatch -> 컴파일 시간에 실행할 함수를 결정함

  2. Dynamic Dispatch -> 런타임 실행할 함수를 결정함

Static Dispatch

컴파일 타임에 컴파일러가 어떤 메소드를 실행할지 주소값을 알고 있음

별도 과정 없이 inline으로 실행됨

아 이 함수는 그 서울시 토비로 103 건물 304호에 Toby라는 사람 있어 그 사람에게 코드 받으면 돼!

Dynamic Dispatch

런타임에 어떤 메소드를 실행할지 Swift는 V-Table을 이용하여 실행할 코드를 결정한다.

V-Table을 찾아서 실행할 코드를 사용시 마다 참조하기에 Static 보다 느리다.

아 이 함수는 그 서울시 토비로 103 건물 어딘가에 Toby라는 사람 찾아서 그 사람에게 코드 갖고와.

어떠한가? 예시 문장을 보자

Static Dispatch는 정확한 주소를 제공함으로 가져오는데 시간이 절약되지만,

Dynamic Dispatch는 건물에 가서 Toby라는 사람을 찾고 갖고 와야한다.

건물이 단층이라면 다행이지만 100층짜리 아파트라면? 엄-청 시간 오래 걸릴 것이다.

그럼 왜 Dynamic을 사용하는 것일까?

\=> 함수 실행요건에 따라서 달라지는 경우가 생길 수 있음

예를 들면, 클래스 상속! 실행 될 때에 따라서 업캐스팅이 되었는지 다운 캐스팅이 되었는지 둘 다 아닌지 상황 판단을 해야함

컴파일에는 미리 결정할 수 없음으로 런타임 시간에 결정해야 함

\=> 외부 라이브러리를 불러 올 때

외부 라이브러리를 불러올 때 Static, Dynamic 선택할 수 있게 만든 라이브러리가 종종 있음

이 때 비용을 생각해가면서 Static, Dynamic을 선택해야 함

엥? 이상하다 Static이 빠르다며 근데 왜 비용을 고려해?

Static으로 분류된 라이브러리를 갖고오면 코드를 통째로 컴파일에 올려버린다.

즉 용량이 되게 커버린다. 그리고 빌드타임이 굉장히 오래 걸린다.

반면, Dynamic은 실시간으로 코드를 가져오기 때문에 용량이 작다.

이러한 장단점을 비교해가며 선택해야 한다.

그럼 예시를 통해서 알아보자

예시를 통한 Static, Dynamic

Protocol

protocol Animal {
    func cry()
}

class Dog: Animal {
    func cry() {
        print("멍멍")
    }
}

class Cat: Animal {
    func cry() {
        print("야옹")
    }
}

let object: Animal = Dog()
object.cry()

위 코드에서 func cry()는 Dynamic일까? -> YES

왜냐하면 func cry()의 같은 경우 어떤 곳에서 코드를 가져와야 할지 확인해야하기에 Dynamic

protocol Animal {
    func cry()
}

extension Animal {
    func birth() {
        print("동물이 탄생했습니다!")
    }
}

그럼 여기서 birth는 Dynamic 일까? Static 일까?

정답은 Static이다. 왜냐하면 protocol 채택하는 곳 상관 없이 모두 동일하게 작동되기 때문이다.

값 타입, Value 타입

값 타임에서는 상속이 불가능하기에 Static Dispatch가 사용

Class

protocol Animal {
    func cry()
}

extension Animal {
    func birth() {
        print("동물이 탄생했습니다!")
    }
}

class Dog: Animal {
    func cry() {
        print("멍멍")
    }

    func birth() {
        print("강아지가 탄생했습니다!")
    }
}

class Cat: Animal {
    func cry() {
        print("야옹")
    }
}


let object: Animal = Dog()
object.cry()
object.birth()

Dog의 cry함수는 Dynamic -> 프로토콜을 채택 받아서 사용하기 때문에

Class + extension 은? 이 같은 경우도 Protocol 과 같이 상속받는 것과 상관 없이 작동하기에 static

그럼 개발자가 확실하게 Static으로 변경할 수 있도록 만들 수 없을까?

그럼 여기서 바로 중요한 키워드가 나옴 바로 final 이라는 키워드임

final 이란?

더 이상 상속하지 않는다면 Dynamic으로 계산할 필요가 없음

  • 그래서 final로 선언한 클래스는 Static으로 작동한다.

스위프트는 역시 똑똑해 WHO(Whole Module Optimization)

모듈 전체를 컴파일 하여서 internal level에서 오버라이딩 되는지 안되는지 추론 하여서 오버라이딩 가능성이 없는 경우 내부적으로 final을 붙여서 계산한다.

  • WHO가 적용될 수 없는 경우 open public추론상 무조건 dynamic으로 한다.

  • 외부에서 사용(public) 외부에서 오버라이딩(open) 가능성이 있음

코드의 dynamic은 무엇일까?

  • Objective-C 런타임 시간에 Dynamic Dispatch를 적용 받기 위해 사용된다.

  • @objc 와 비슷하다.

    *objc는 Objective-C에서 API를 사용할 수 있도록 하는 속성


참조한 사이트