본문 바로가기
Swift

[Swift] Class에서 Final, Private를 쓰면 정말 성능이 향상될까?

by Jimmy_iOS 2023. 6. 30.

Class의 성능을 향상하기 위해서는 final 및 private을 사용하는 것이 권장됩니다.

Swift의 Class와 Protocol은 모두 참조 타입(reference type)입니다.

Class는 상속과 오버라이드가 가능해 컴파일 타임에서 적절한 타입을 추론할 수 없어 런 타임에서 결정되게 됩니다.
런 타임에서 Class는 V-Table, Protocol은 W-Table을 사용하여 Dynamic Dispatch를 수행합니다

 

앱 생성 시 대개 ViewController를 상속받아서 UIViewController 클래스와 함께 Dynamic Dispatch가 작동하도록 코드를 작성합니다.

Dynamic Dispatch는 Static Dispatch에 비해서 성능이 뒤떨어질 수밖에 없는데요. 

 

애플에서는 Class의 성능 향상을 위해서 기본적으로 Struct의 사용을 추천합니다.

그럼에도 불구하고 Class를 사용하는 경우라면 final 키워드를 사용해 상속과 오버라이딩의 가능여부를 없애고

private 키워드를 사용해 런타임에 동적으로 호출될 가능성을 줄여줄 수 있습니다.

 

Static Dispatch vs Dynamic Dispatch

final 키워드와 private 키워드를 사용했을 때 컴파일러는 정적 디스패치와 동적디스패치를 어떻게 결정하는지 궁금해졌는데요.

Swift의 중간 언어인 SIL(Swift Intermidiate Language)을 사용하면 컴파일러의 처리 과정을 분석할 수 있다고 합니다.

 

 

그래서 SIL을 사용해 몇 가지 테스트를 진행해 볼 건데요.

1. Protocol을 채택하면 프로퍼티와 메서드는 W-Table에서 작동하는가?

2. Protocol을 Extension에서 구현하면 Static Dispatch로 작동하는가?

3. Parent Class를 상속받으면 Child Class에서 V-Table은 어떻게 생겼는가?

4. final 키워드를 붙이면 Static Dispatch로 작동하는가?

5. private 키워드를 붙이면 Static Dispatch로 작동하는가?

 

다음은 테스트 결과입니다.

 

Class B에서 A 프로토콜의 a 프로퍼티와 aa 메서드를 구현했습니다.

Extension A에서는 b 프로퍼티와 bb 메서드를 구현했습니다.

SIL로 코드를 분석한 결과, Class B의 a 프로퍼티와 aa 메서드는 V-Table에 올라가 있는 것으로 보입니다.

Protocol A의 a 프로퍼티와 aa 메서드는 W-Table에 올라가 있는 것으로 보입니다.

Protocol을 채택하면 프로퍼티와 메서드는 W-Table에서 작동하는가?

클래스가 프로토콜을 채택한 경우, 해당 클래스의 속성 및 메서드는 V-Table과 함께 작동한다는 것으로 판단됩니다.

Protocol을 Extension에서 구현하면 Static Dispatch로 작동하는가?

Extension A의 b 프로퍼티와 bb 메서드는 V-Table과 W-Table에 모두 없는 것으로 보아 Static Dispatch로 구동될 것으로 판단됩니다.


Parent Class를 상속받으면 Child Class에서 V-Table은 어떻게 생겼는가?

부모 클래스의 Static 프로퍼티는 V-Table에 없습니다. 즉 Static 프로퍼티는 Static Dispatch로 작동하는 것으로 판단됩니다.

 

저장 프로퍼티, private 저장 프로퍼티, 메서드, private 메서드는 모두 V-Table에 올라가 있는 것을 확인할 수 있습니다.


private 키워드를 붙이면 Static Dispatch로 작동하는가?

클래스를 생성하고 프로퍼티와 메서드를 만들었으며, private 키워드를 붙인 프로퍼티와 메서드를 구현했습니다.

SIL 코드에서는 private 키워드 여부와 상관없이 모두 V-Table에 올라갑니다.

private 키워드를 붙이면 컴파일러가 Override될 가능성이 없으면 Final로 인식한다고 합니다.
그러나 이 코드에서는 Final로 인식되지 않는 것 같습니다.

 

private 키워드가 지정되어 있더라도 Static Dispatch와 함께 작동하지 않을 수 있습니다.
자식 클래스의 V-Table은 부모 클래스에서 상속받은 속성 및 메서드와 해당 자식 클래스에서 구현한 속성 및 메서드가 모두 포함됩니다.

 

private 키워드를 사용하면 해당 멤버는 같은 파일 내에서만 접근 가능하고, 외부로 노출되지 않습니다.
하지만 Swift 컴파일러는 private 멤버를 포함한 모든 멤버를 V-Table에 내부적으로 추가합니다.
왜냐하면 private 멤버를 호출하는 경우라 할지라도 해당 코드가 런타임에 동적으로 호출될 가능성이 존재하기 때문입니다.

 

예를 들어 private 멤버를 포함한 클래스를 상속한 자식 클래스에서 해당 private 멤버를 오버라이드할 경우,
해당 private 멤버 호출은 다형성(polymorphism)을 지원하기 위해 동적 디스패치(dynamic dispatch)로 처리될 수 있습니다.


그러므로 private 멤버를 포함한 클래스나 구조체의 인스턴스가 생성될 때, 컴파일러는 V-Table에 private 멤버를 포함시킵니다.
실제로 private 멤버가 직접 호출되지 않더라도, Dynamic Dispatch를 지원하기 위해 컴파일러는 해당 멤버를 V-Table에 포함시키기 때문입니다.


final 키워드를 붙이면 Static Dispatch로 작동하는가?

위와 같은 코드에서 클래스 앞에 final 키워드를 추가했습니다.

SIL 코드를 보면 모든 프로퍼티와 메서드가 V-Table에 없는 것으로 보아, Static Dispatch로 작동하는 것으로 판단됩니다.


SIL에 대한 자세한 내용은 우아한 기술 블로그에서 설명되어 있습니다.

https://techblog.woowahan.com/2564/

 

[번역] SIL(Swift Intermediate Language), 일단 시작해보기까지 | 우아한형제들 기술블로그

{{item.name}} 원문 Swift中間言語の、ひとまず入り口手前まで 이 글은 훌륭한 Swift, iOS 개발자이자 교육자인 Tomohiro Kumagai씨가 qiita에 올리신 글을 허가를 얻어 배포합니다. *(주) 내용은 최대한 정확

techblog.woowahan.com