Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method Dispatch in Swift #104

Closed
Youngminah opened this issue Dec 29, 2021 · 2 comments
Closed

Method Dispatch in Swift #104

Youngminah opened this issue Dec 29, 2021 · 2 comments

Comments

@Youngminah
Copy link
Owner

Youngminah commented Dec 29, 2021

image

메모리에서 어떤 메커니즘으로 함수를 호출 하는지 알아보자





메소드 디스패치란?

메소드 디스패치: 메소드를 실행할 때 어떤 명령어을 실행해야 하는지 결정하도록 돕는 메커니즘
how a program selects which instructions to execute when invoking a method



글 시작전 알아야 할 것

Static Dispatch : 값 타입(value types) 과 참조 타입(reference types) 모두 지원한다.
Dynamic Dispatch : 반면에, 동적디스패치는 참조 타입(reference types)만 지원한다.
이러한 이유는 동적디스패치를 위해서는 상속이 필요한데 값 타입은 상속을 지원하지 않기 때문이다.


크게 보면 , Dispatch 기술은 2가지 타입(Static and Dynamic)이 아닌 4가지 타입으로 나뉜다.

  1. Inline (Fastest)
  2. Static Dispatch
  3. Virtual Dispatch
  4. Dynamic Dispatch (Slowest)

이 중에 어떤 기술을 이용할 지는 컴파일러에 달려있다.
가장 빠른 Inline으로 올라가거나 가장 느린 Dynamic으로 내려오는 등 필요에 따라 결정한다.



Objective-C , Swift

Objective-C

디폴트로 Dynamic Dispatch를 지원한다.

  • 장점
    • 다형성의 형태로 프로그래머에게 상당한 유연성을 제공한다.
    • 이미 존재하는 메소드 등을 서브 클래싱, 재정의 할 수 있기 때문임.
  • 단점
    • 런타임에 필요한 오버헤드가 많아지므로, 비용(Cost)이 많이 든다.

Swift

  • Dynamic Dispatch를 위해서 우리는 상속을 사용한다.
  • 기본 클래스(base class)를 서브 클래싱하고 기본 클래스에 존재하는 메소드를 재정의(override)한다.
  • 또한, 우리는 dynamic 키워드를 사용하고 @objc 키워드를 prefix하여 Objective-C 런타임에 메소드를 노출시키기도 한다.

  • Static Dispatch를 위해서 우리는 final, static 키워드를 통해 클래스의 경우 추가적인 상속이나 재정의가 불가능하도록 만든다.


Static vs. Dynamic

Dynamic

  • 런타임 오버헤드의 비용을 증가시켜 언어 표현력(language expressivity)을 향상시킨다.
  • 즉, 메소드를 호출할 때마다, 컴파일러가 특정 메서드의 구현을 확인하기 위해 ,
  • witness table (가상 테이블 또는 다른 언어의 디스패치 테이블) 이라고 부르는 내부를 조사해야 한다,
  • 컴파일러는 수퍼클래스의 구현을 참조하는지 아니면 하위 클래스의 구현을 참조하는지 판별할 필요가 있다.
  • 그런데, 메모리에 모든 오브젝트는 런타임에 올라가게 되므로 컴파일러는 이러한 과정을 런타임에 해야할 수 밖에 없다.

Static

  • 반면 정적 디스패치는 이렇지 않아도 된다.
  • 컴파일러는 컴파일 타임에 어떤 메소드 구현이 호출되어야할지 알고있다.
  • 그러므로 컴파일러는 여러 최적화 기법을 적용할 수 있고 혹은 코드를 Inline으로 변환(convert)할 수도 있다.
  • 따라서 전체적인 프로그램의 속도를 상당히 빠르게 향상시킬 수 있다.


위의 내용 직관적 정리

Static Dispatch (or Direct Dispatch)

  • 컴파일러가 컴파일타임에 명령어가 어디 있는지 결정이 되어 찾을 수 있으므로, 동적 디스패치와 비교할 때 훨씬 빠르다.
  • 따라서, 함수가 호출 되면 , 컴파일러는 함수의 메모리 주소로 직접 점프하여 작업을 수행한다.
  • 인라인과 같은 특정 컴파일러 최적화와 큰 성능 향상이 될 수 있다.

Dynamic Dispatch

  • 앞에서 설명한 것처럼 이러한 유형의 디스패치에서는 구현이 컴파일 시간 대신 런타임에 선택되어 오버헤드가 추가된다.


그렇다면, 비용이 많이 드는 Dynamic Dispatch 왜 사용할까? ❗️

image

  • OOP의 특징인 유연성 (Flexibility) 때문이다.
  • 대부분의 OOP 언어는 다형성(객체 지향 3대 원칙)이라는 특징때문에 Dynamic Dispatch를 지원한다.


Dynamic Dispatch 2가지 타입

Table Dispatch

  • 이 디스패치 기법은 테이블을 사용한다.
  • 여기서 테이블이란 함수 포인터로 이루어진 배열을 의미하며 Witness Table (혹은 Virtual Table)이라고 불린다.
  • 특정 메소드의 구현을 찾는(Look up) 용도로 사용
  • Witness Table이 어떻게 작동하는가?
    • 모든 서브클래스는 각자의 테이블을 갖고있다.
    • 이 테이블은 서브클래스가 재정의한 모든 메소드에 대해 다른 함수포인터를 갖고있다.
    • 서브클래스가 새로운 메소드를 추가하면 해당 메소드에 대한 함수포인터가 배열 끝에 추가된다.
    • 컴파일러가 런타임에 이 테이블을 사용하여 어떤 메소드를 호출할지 결정한다.
  • 정적 디스패치와 달리 컴파일러가 먼저 테이블로부터 메모리 주소를 읽고(read)
  • 해당 위치로 이동(jump)해야 하므로 두번의 연산이 요구된다.
  • 이것이 정적 디스패치보다 느린 이유다. (메시지 디스패치보다는 여전히 빠르다)

Message Dispatch

  • 이 동적 디스패치 기법은 가장 동적이라고 할 수 있다.
  • 사실 이 기법은 최적화 측면을 제외하면 매우 좋아서
  • Cocoa 프레임워크에서도 KVO, 코어데이터와 같은 곳에서 자주 사용된다.
  • 이 방법은 런타임에 메소드의 기능(functionality)를 바꾸는 Method Swizzling을 가능하게 한다.
  • Swift 컴파일러는 이 기법을 Obejctive-C 런타임을 사용해 achieve한다.
  • Swift 4.0 이후부터는 @objc 키워드를 마크해주어야한다.


예제

값타입

struct Person {
    func isIrritating() -> Bool { } // Static
}

extension Person {
    func canBeEasilyPissedOff() -> Bool { } // Static
}
  • 구조체와 열거형은 값타입이며 상속이 불가능하므로 컴파일러는 당연히 정적 디스패치를 사용할 것이다.

프로토콜

protocol Animal {
    func isCute() -> Bool { } // Table
}

extension Animal {
    func canGetAngry() -> Bool { } // Static
}
  • 여기서 키포인트는 extension 내부에서는 항상 정적 디스패치가 이루어진다는 것이다.
  • extension에서는 재정의가 불가능하기 때문에 그렇다.

클래스 (참조타입)

class Dog: Animal {
    func isCute() -> Bool { } // Table
    @objc dynamic func hoursSleep() -> Int { } // Message
}

extension Dog {
    func canBite() -> Bool { } // Static
    @objc func goWild() { } // Message
}

final class Employee {
    func canCode() -> Bool { } // Static
}
  • 보통의 메소드 선언은 프로토콜과 동일하게 동적 디스패치(그 중에서도 테이블 디스패치)를 따른다.
  • Objective-C 런타임에 노출시키면 (@objc 키워드를 통해) 메소드는 메시지 디스패치를 사용한다.
  • 하지만 final 키워드로 마크되어 서브클래싱이 불가능하다면 클래스라도 정적 디스패치가 가능하다.

image




참고자료

Static vs Dynamic Dispatch in Swift: A decisive choice
Method Dispatch in Swift 번역본

@Youngminah Youngminah changed the title Method Dispatch in Swift Memory Method Dispatch in Swift Jan 12, 2022
@Youngminah Youngminah changed the title Memory Method Dispatch in Swift Method Dispatch in Swift Jan 12, 2022
@Lautner-kwangho
Copy link
Collaborator

Static Dispatch : 갑 타입(value types)
-> 오타 수정
Static Dispatch : 값 타입(value types)

@Youngminah
Copy link
Owner Author

Thank you ⭐️⭐️⭐️⭐️⭐️ @Lautner-kwangho

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants