final?

final 키워드는 기본적으로 상속과 오버라이드를 제한하는 역할로 사용된다. 그렇다면 상속과 오버로드를 막아야하는 경우가 아니라면 사용하지 않을까? final 키워드를 적재적소에 사용하면 런타임 성능을 향상시킬 수 있다고 한다.
final 키워드를 클래스에 사용할 경우 내부의 프로퍼티와 함수 모두 상속이 불가능해진다. 물론 특정 프로퍼티나 메소드에도 직접 final 키워드를 붙일 수 있다. 메소드나 프로퍼티의 오버라이드를 예상하지 않는다면 final 키워드를 붙이는 것이 좋다.

Dispatch

Dispatch는 어떤 메소드를 호출하고 실행할지를 결정하는 매커니즘으로 Static Dispatch / Dynamic Dispatch 두가지가 존재한다.

Static Dispatch

Static Dispatch는 컴파일 타임에 실행할 메소드를 미리 결정하고 결정된 메소드를 런타임에 실행한다.

Dynamic Dispatch

Dynamic Dispatch는 함수들에 대한 주소인 포인터를 vtable에 유지하고 런타임에 vtable을 참조하여 실행할 메소드를 결정한다. Static Dispatch에 비해서 런타임에 실행해야하는 작업이 더 있으므로 (호출할 메소드를 찾아야함) 성능적으로 손해를 보게 된다.

in Swift

valueType은 메소드가 오버라이드되지 않으므로 Static Dispatch를 사용한다. 어떤 파일에서 호출되어도 해당 타입에서 호출되므로 런타임시에 메소드를 추적할 필요가 없다.
하지만 referenceType의 경우 상속이 가능하다. 상속을 하면 오버라이드 역시 가능하다. valueType과는 다르게 어떤 타입에서 호출된 메소드인지 추적이 필요하다는 뜻이다. 어떤 클래스에서 호출된 메소드가 해당 클래스에서 호출되는 것인지 그 클래스가 상속받은 부모 클래스에서 호출되는 것인지 알 수가 없기 때문에 컴파일 시점에 이를 결정할 수 없고 런타임 시점에 결정할 수 있다. 이런 방식이 Dynamic Dispatch이므로 referenceType의 경우에는 특별한 작업을 통한 것이 아니면 Dynamic Dispatch를 사용한다고 보면된다.

런타임 성능 향상을 해보자

referenceType은 기본적으로 Dynamic Dispatch로 동작한다는 것을 알았으니 이를 Static Dispatch로 동작하게 만들어서 런타임 성능을 향상 시킬 수 있다는 것이 이해가 되었다. 상속이 되지 않는다는 확신이 있는 referenceType에 final 키워드를 붙이면 메소드가 오버라이드 되지 않을 것이고 기존에 Dynamic Dispatch 동작이 Static Dispatch로 대체될 것이다. 이러면 호출되는 메소드가 컴파일 시점에 결정이 되므로 런타임 성능이 향상될 것이다.
final 키워드 뿐만이 아니라 private / fileprivate 접근 제어자를 통하여서도 Dynamic Dispatch를 줄일 수 있다고 한다. private가 붙은 클래스, 프로퍼티, 메소드등도 접근이 선언된 타입에서만 가능하고 fileprivate는 해당 파일에서만 가능하므로 작성된 곳에서 오버라이드되지 않았다면 자동으로 final 키워드를 추론하여 Static Dispatch를 적용한다고 한다.
코딩을 하며 private 접근제어자는 제때 써보려는 인식이 있어서 예전보다 많이 손에 익었지만 final 키워드는 사용해본적이 없었다.. 상속과 프로토콜을 사용하여 코드를 고도화 해보고 싶은 생각이 항상 있는데 상속여부를 판단하여 final 키워드까지 잘 사용한다면 성능적 이점도 챙겨갈 수 있을 것 같다.

태그: ,

카테고리:

업데이트:

댓글남기기