Method Dispatch
실제로 Method는 CPU에대한 명령어이며, 실제론 코드 영역에만 존재한다. 그렇기 때문에 코드 영역에 있는 메서드를 실행시키려면 메서드 주소가 필요하다. 그런데 그 메서드 주소를 기억하고, 어떤 메서드를 실행시킬지에 관련된 내용이다.
한 마디로
"어떤 메서드를 부를지 선택하는것"
Swift가 함수(메서드)를 실행시키는 방법은 3가지가 있다.
컴파일타임/런타임
- 컴파일타임: 작성된 소스코드는 컴파일이라는 과정을 통해 기계어 코드로 변환되어 실행 가능한 프로그램이 된다. 이러한 편집 과정을 컴파일타임(Compiletime)이라고 부른다.
- 런타임: 컴파일과정을 마친 프로그램이 사용자에 의해 실행되며, 이러한 프로그램이 동작되는 때를 런타임(Runtime)이라 부른다
@컴파일타임
컴파일 타임에 정해지는 방식
Direct Dispatch(정적(직접) 디스패치/Static Dispatch)
- 컴파일 시점에 함수의 메모리 주소를 삽입하거나, 함수의 명령코드를 해당위치에 복사해서 넣어버린다(in-line).
- 컴파일러가 명령(instructions)들의 위치를 다 알고있다. 함수가 호출되면 컴파일러는 수행할 함수의 메모리 주소로 바로 jump한다.
- 가장 빠른 방법이다.
만약 함수의 명령코드를 해당위치에 복사해서 넣어버렸다면, 코드를 찾아갈 필요가 없으므로 0.0ns가 걸린다.
함수의 메모리 주소를 찾아간다면 찾아가는 시간정도인 2.13ns정도의 시간이 걸린다.
다른 Dispatch에 비해 빠른 속도다.
사용: Value Type
구조체/열거형
상속/다형성의 장점을 누릴 수 없다. ==> 상속/다형성이 필요없을 때 사용한다.
@런타임
실행중인 런타임 동안에 정해지는 방식
Table Dispatch(동적 디스패치/Dynamic Dispatch)
- 동적 디스패치의 가장 기본적인 구현방식
Table Dispatch는 테이블을 사용하는데 함수의 메모리주소인 포인터로 이루어진 배열을 의미한다.
- 모든 서브클래스는 각자의 테이블을 가지고있다.
- 테이블에는 서브클래스가 재정의한 모든 메서드에 대해 각각 함수 포인터를 갖고있다.
- 서브클래스에서 새로운 메서드를 추가하면 해당 메서드에 대한 포인터가 배열 끝에 추가된다.
- 컴파일러가 런타임에 이 테이블을 사용하여 어떤 메서드를 호출할지 결정한다.
클래스에서 사용되는 테이블 = Virtual Table
프로토콜에서 사용되는 테이블 = Witness Table(프로토콜을 채택했을 때 어떤 메서드를 실행할지 저장하는 테이블)
이름만 다른, 같은 역할을 수행하는 테이블이다.
프로토콜의 타입으로 저장이 되었다면 Winess테이블, 클래스 타입으로 저장이 되었다면 Virtual Table을 참조한다.
Message Dispatch(메시지 디스패치)
- Objective c에서 사용되던 방식이다.
자기자신이 재정의하거나 새로정의한 메서드만 테이블에 유지한다.
대신 부모클래스의 포인터를 가지고 있어서 부모클래스의 메서드들은 부모클래스를 찾아가서 실행한다.
총 정리
Value Type(구조체 열거형):
Valut Type에 메서드를 작성하면 Direct Dispatch방식으로 작동한다. 어차피 상속 구조가 없기 때문에 테이블이 필요없다.
상속이 있을때만 메서드를 재정의하고 테이블을 생성하는데 상속개념이 없으므로 테이블이 필요없다.
Protocol
- 본체에서 요구사항으로 요구하는 메서드를 구현하면 Table Dispatch방법으로 Witness테이블을 만든다.
- 프로토콜의 확장에서 요구사항으로 선언되어있지 않은 메서드를 구현할 때에는 DirectDispatch로 동작한다.
Class
- 본체에서 Virtual Table이라고 하는 Table Dispatch로 동작한다.
- 클래스의 Extension에서, 상속할 때 재정의가 불가능하다(원칙). 상속이 불가능하기 때문에 Direct Dispatch로 동작한다.
- 클래스의 Extension에서, 원칙적으론 상속이 불가능 하지만, @objc dynamic키워드를 통해 objective-c 방식으로 바뀌는데, 이렇게 되면 Message Dispatch방식으로 작동하게 되고 이렇게되면 Extension에서 정의한 메서드의 상속과 재정의가 가능해진다.
- 클래스의 상속을 막기위해 final키워드를 사용할 수 있다. 이렇게 되면 상속이 불가능 하기 때문에 Direct Dispatch로 동작한다.