Swift에서 Method Dispatch는 메서드 호출 시 어떤 방식으로 호출할 메서드가 결정되고 실행되는지를 의미한다.
Method Dispatch의 종류에는 Static dispatch, Dynamic dispatch가 있다.
Static Dispatch는 컴파일 타임에 호출할 메서드가 결정된다. 즉, 호출할 메서드의 구현체가 컴파일 시점에 고정된다는 것을 의미하며 컴파일러가 소스 코드를 분석하여 어떤 메서드가 호출될지 이미 알고 직접 점프하는 코드를 생성한다.
Static Dispatch는 inlining을 적용해서 코드 최적화를 수행할 수 있다.
💡inlining : 함수 호출을 제거하고 함수의 본문을 호출하는 코드(함수 구현 코드)로 직접 대체하는 최적화 기법
Dynamic Dispatch는 컴파일 타임에 어떤 메서드를 호출할지 결정할 수 없다(가시성이 좋지 않다). 어떤 구현체인지 파악이 되지 않기 때문에 런타임에 구현체들을 탐색해서 맞는 구현체로 점프한다. 이러한 특성 때문에 컴파일러를 통한 코드 최적화가 불가능하다.
Dynamic Dispatch는 다형성이 적용된 경우 즉, 상속 관계에서 메서드 오버라이딩이 사용될 때 주로 나타난다.
class Animal {
func sound() {
print("Animal sound")
}
}
class Dog: Animal {
override func sound() {
print("Woof!")
}
}
class Cat: Animal {
override func sound() {
print("Meow!")
}
}
let animals: [Animal] = [Dog(), Cat(), Animal()]
for animal in animals {
animal.sound()
}
animals 배열의 요소들은 컴파일 타임에 모두 Animal 타입으로 보인다. 하지만 실제로 배열에는 Dog, Cat, Animal의 인스턴스가 들어있다. 따라서 animal.sound() 호출 시 런타임에 실제 객체의 타입에 따라 적절한 메서드를 호출해야 하기 때문에 Dynamic Dispatch가 사용되고 아래의 결과가 출력된다.
Woof!
Meow!
Animal sound
inlining을 적용할 수 없다.final 키워드가 붙은 메서드 : 상속을 의도하지 않음을 final 키워드로 명시