오늘은 Swift에서 사용되는 final keyword에 대해 정리해보려고 합니다.
기존에 알고 있던 지식은 단순히 상속을 막는 기능 이라고 알고 있었고,
또한 Swift Documents에서도 Preventing Overrides 라는 주제로 final
키워드를 소개하고 있습니다.
You can prevent a method, property, or subscript from being overridden by marking it as final. Do this by writing the final modifier before the method, property, or subscript’s introducer keyword (such as final var, final func, final class func, and final subscript).
Any attempt to override a final method, property, or subscript in a subclass is reported as a compile-time error. Methods, properties, or subscripts that you add to a class in an extension can also be marked as final within the extension’s definition.
You can mark an entire class as final by writing the final modifier before the class keyword in its class definition (final class). Any attempt to subclass a final class is reported as a compile-time error.
(출처 : https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html#ID202)
근데 상속을 막아서 무슨 이득이 있을까요 ..?? 굳이 안막아도 되지않을까 ..????
하지만 갓갓 개발자님들이 없어도 되는거 만들지 않았겠지 ㅋㅋㅋ
찾아보면 바로 나옵니다.
출발~ 🏃♂️
siwft 깃허브의 OptimizationTips.rst 파일에는 Writing High-Performance Swift Code 라는 주제를 다루고 있습니다.
그중에 바로 두둥 우리의 주제가 들어있습니다.
Advice: Use 'final' when you know the declaration does not need to be overridden
Swift의 고성능을 위해서는 상속 되지 않는다고 알고 있는 declaration(선언)은 final
키워드를 사용하라고 나와 있습니다.
원문을 자세히 보면
The final keyword is a restriction on a declaration of a class, a method, or a property such that the declaration cannot be overridden.
This implies that the compiler can emit direct function calls instead of indirect calls.
마지막줄이 중요해 보이네요
"컴파일러가 indirect call 대신 direct function calls을 제출할 수 있다는것을 함축하고 있습니다."
(🦶해석 죄송합니다)
그 아래에 적혀있는 예시입니다.
For instance in the following C.array1 and D.array1 will be accessed directly*.
In contrast, D.array2 will be called via a vtable:
final class C {
// No declarations in class 'C' can be overridden.
var array1: [Int]
func doSomething() { ... }
}
class D {
final var array1: [Int] // 'array1' cannot be overridden by a computed property.
var array2: [Int] // 'array2' *can* be overridden by a computed property.
}
func usingC(_ c: C) {
c.array1[i] = ... // Can directly access C.array without going through dynamic dispatch.
c.doSomething() = ... // Can directly call C.doSomething without going through virtual dispatch.
}
func usingD(_ d: D) {
d.array1[i] = ... // Can directly access D.array1 without going through dynamic dispatch.
d.array2[i] = ... // Will access D.array2 through dynamic dispatch.
}
C class의 array1과 D class의 array1은 direct access된다고 합니다.
코드를 보면 C class는 이 붙어있습니다. 즉, 상속이 불가능하며, direct call 이 이루어 질 수 있습니다.
class가 이기 때문에 C class 안에 정의된 모든 property와 method는 상속이 불가능합니다.
즉 final
이 붙은것 과 마찬가지입니다. 따라서 array1 property, doSomething method 모두 direct call이 이루어 질 수 있습니다.
D class는 final
이 붙어있지 않습니다. 이는 상속이 됐을 수도 있고, 안되었을 수 도 있습니다.
D class의 property를 보면 array1은 이지만, array2의 경우 final
이 없습니다. 따라서 D.array1은 direct call이 가능하지만, D.array2는 direct call이 불가능합니다.
indirect call은 vtable을 통해 이루어진다고 되어있습니다.
(In contrast, D.array2 will be called via a vtable)
vtable이란 dynamic dispatch를 지원하기 위해 프로그래밍 언어에서 사용되는 매커니즘 입니다.
정확한 내용은 가상 메소드 테이블 문서를 참조해주세요.
따라서,
vtable을 사용한 indirect call은 추가적인 역참조를 필요로 하기 때문에 direct call 함수 호출보다 느립니다.
결론은
상속되지 않는 class, property, method는 final
키워드를 사용해서 dynamic dispatch를 줄여 성능을 향상시킬 수 있다는 것입니다!
있는건 알았지만 자주 사용하지 않았던 final
키워드 !!
키워드를 사용하는 참된 의미를 알았으니 이제 자주 사용할 것 같습니다 ..ㅎ
그런데.. Writing High-Performance Swift Code을 보다보니 Reducing Dynamic Dispatch라는 중주제가 있네요 ?! 😵
final을 사용하는 것은 Reducing Dynamic Dispatch 중 하나의 방법이였습니다;;
그러므로 다음 주제는 Dynamic Dispatch입니다. 하하핳하하
그리고!!
틀린부분에 대한 지적은 언제나 환영입니다.👍
읽어주셔서 감사합니다!! 🙇♂️
그럼 다음 포스팅에서 만나요 !! 안녕 👋
참조 :
https://velog.io/@ryan-son/Swift-final-키워드-왜-사용하는걸까
https://github.com/apple/swift/blob/main/docs/OptimizationTips.rst#dynamic-dispatch