본문 바로가기
iOS/Swift

Swift) final

by Jiseong 2022. 2. 20.

final?

출처

Swift에선 상속을 지원한다!!

 

final 키워드를 클래스에 앞단에 붙이면 해당 클래스는 더 이상 상속하지 않겠다.를 의미한다.

 

상속하지 않겠단걸 보고, 어.. 클래스만 상속 가능하니깐 클래스만 final을 사용할 수 있겠네? 하면.. 오산이다.

재정의가 더 이상 필요없다고 생각하거나, 재정의를 금하고싶은 경우에 사용할 수도 있다.

 

아무래도 상속받아서 하는 것이 재정의다보니 같은 것으로 생각하면 될 것 같다.

 

그렇다면 final 키워드를 붙여주면 얻는게 무엇일까?

 

이는 클래스와 관련이 있는데.. 클래스는 Dynamic Dispatch로 동작한다.

 

Dynamic Dispatch는 런타임시 VTable을 통해 메서드를 찾아 실행하기 때문에 "찾는 것"에서 성능상 손해를 발생시킨다.

 

클래스가 Dynamic Dispatch로 동작하는 이유는 상속, 즉 오버라이딩 가능성 때문인데, final 키워드를 사용하여 상속, 오버라이딩을 막아준다면 이는 Static Dispatch로 동작하여 아까 말한 "찾는 것"에 대한 오버헤드를 제거할 수 있다.

 

대충~~ 만든 예제를 보자 

class AAA {
    func AAAHello() {
        print("Hi")
    }
}

class AAASon : AAA {}

var aaa: AAA = AAASon()
aaa.AAAHello() 

aaa는 AAAHello를 호출할 때, AAA를 참조할지, AAASon을 참조할지 런타임 시점에 VTable에서 찾으므로, 아까 말한 오버헤드가 발생한다.

 

만일 상속, 오버라이딩이 필요한 경우엔 Dynamic Dispatch가 불가피하겠지만, 필요하지 않다면?? Static Dispatch로 동작하면.. 좋겠지????????

 

그럼.. 바꿔주는 방법에 대해 알아보자 아 근데.. final글인데.. 다른 방법까지 쓰기가... 뭐해서 메인이 아닌 방법은 좀 간추려 설명하겠다.

final

본격적으로 이에 대해 알아보자.. 위에서 대부분 말한 것 같지만..

 

final 키워드는 프로퍼티, 클래스, 메서드 앞단에 붙일 수 있다.

 

final 키워드가 붙은 클래스의 경우!! 클래스 자체가 상속 불가능한 객체가 된다.

 

프로퍼티, 메서드의 경우 해당 프로퍼티, 메서드를 갖고 있는 클래스를 상속받은 하위클래스에서 오버라이딩을 할 수 없게한다!!

우째되나 볼까?

 

먼저 클래스

파이널 클래스는 상속 불가능하다! 라는 오류를 뿜는다.

 

이때는 클래스가 어떤 메서드나 프로퍼티를 갖고있든 클래스 자체가 상속불가, 즉 오버라이딩이 불가능하기 때문에 파이널 클래스 내부 메서드, 프로퍼티도 Static Dispatch로 동작하게 된다.

 

약간.. 음.. private extension하면 익스텐션 내부에 있는 연산 프로퍼티나, 메서드들이 같이 private 이 되는 것과 같다고 생각하면 편하다.

 

클래스 자체를 파이널로 두지 않고 내부 메서드나 프로퍼티에만 키워드를 붙이면..

 

파이널 키워드를 붙인 메서드나 프로퍼티만 Static Dispatch로 동작한다. 

 

왜?? 얘넨 오버라이딩이 불가능하니깐.. 근데 파이널을 안붙인 프로퍼티나 메서드는 오버라이딩이 되거든.. 그럼 걔네들은 Dynamic Dispatch로 동작하겠지?!

class AAA {
    final var num1 = 1 // Static Dispatch
    var num2 = 2  // Dynamic Dispatch
    
    final func AAAHello() {  // Static Dispatch
        print("Hello")
    }
    func AAAHi() { // Dynamic Dispatch
        print("Hi")
    }
}

다시 한번 정리하자면.. final 키워드를 붙이면?? Static Dispatch가 됨으로써 상속, 오버라이딩 가능성을 제거하여 런타임동안 원본 메소드를 사용할지, 오버라이딩 된 메서드를 사용해야될지를 찾지않아도 되고, 이로써 컴파일 시점에 메서드 인라이닝 되어 해당 메서드는 런타임에 메서드 호출에서 다른 추가 작업(오버헤드(VTable 뒤지기))을 하지 않아도 되어 성능을 향상시킬 수 있다.

메서드 인라이닝(Method Inlining)은 메소드를 호출 할 때 해당 메소드로 이동하지 않고 메소드의 결과값을 바로 반환하여 성능을 향상 시키는 것, 그니깐 찾질 않는다는 것임

두번째 방법은....

 

private이 있는데 이를 붙이면.. 컴파일러는 private 키워드가 붙은 것들이 참조될 수 있는 곳, 즉.. 동일 파일 내부나 그렇겠지? 

 

여튼 컴파일러는 private 키워드가 붙은 것들이 참조될 수 있는 곳에서 해당 키워드가 붙은 것들이 오버라이딩 되냐 안되냐를 판단하여!!

 

오버라이딩 되지 않는다면, 스스로 final 키워드를 내부적으로 붙여..? Static Dispatch로 동작한다!!!!!!!

 

두번째 방법에 대한 더 자세한건.. 조만간 Swift Performance에 대한 글을 쓸건데, 거기서 Dispatch고 뭐고 쭉 같이 공부하고, 기록해볼 것이다..

 

졸려 죽겠다!!!!!!!!!!

댓글