Danny의 iOS 컨닝페이퍼
article thumbnail

시작

공식문서에서 MapKit의 Annotation 예제를 살펴보던 중

@objc dynamic var라는 키워드가 나오더라고요.

annotation 예제 공식문서

MKAnnotation - coordinate 공식문서

 

Realm에서도 이런 키워드를 봤던 거 같은데,

그래서 오늘 간단히 한번 정리해 보고 넘어가려고 글을 적게 됐습니다.

 

 

정적(static) / 동적(dynamic) 디스패치

들어가기 전에 간단히 차이섬을 알아봅시다.

 

👉 정적(static) 디스패치

 

컴파일러가 컴파일 시점에 호출해야 할,

메서드의 실제 위치를 미리 알고 있기 때문에, 매우 빠르게 동작합니다.

 

컴파일 타임에 결정이 되기 때문에, 상대적으로 속도 빠릅니다.

모든 구조체는 이와 같이 동작합니다.

 

대신 클래스에서는 상속을 못하게 되므로 객체지향 프로그래밍의

다형성을 구현하지 못하죠. 

 

 

👉 동적(dynamic) 디스패치

 

런타임 시점에 호출될 메서드 또는 속성을 결정합니다.

(오직 class에서만 사용 가능)

 

정적 디스패치와 반대로

런타임 시점에 호출할 메서드를 알아내기 때문에,

상속 관계에서 오버라이드(override)한 자식 클래스의 메서드를 호출가능 해집니다.

대신 어떤 메서드가 호출될지 정확히 모르므로 성능 및 속도가 떨어지죠.

 

조금 더 자세하게 말하면,

각 클래스는 vTable을 생성하여 이를 객체의 인스턴스에 연결합니다.

객체가 메서드를 호출할 때마다 해당 vTable에서는

"부모 메서드인가? 자식 메서드인가?" 위치를 찾은 후 메서드를 실행하게 되므로

속도가 상대적으로 떨어지게 되는 거죠.

 

동적(dynamic) 디스패치가 필요하지 않은 경우의 최적화 방법으로는

final 키워드를 사용 ("override가 필요 없다" 명시적으로 표시. 최적화 가능),

private 키워드 사용(내부에서만 참조를 보장) 등이 있습니다.

 

 

@objc dynamic

swift에서는 기본적으로 정적 디스패치를 사용하지만,

Objective-C의 동적 디스패치 기능을 사용하기 위해 사용됩니다.

 

Objective-C와 상호 운영성을 유지하게 도와주죠.

 

@objc dynamic var를 사용하게 되면

런타임 시 동적으로 동작이 결정이 되므로 프로퍼티가 변경되었을 때,

KVO(Key-Value Observing)를 통해 관찰이 가능해집니다.

 

런타임에서 프로퍼티의 값이 변경될 때마다,

시스템이 자동적으로 프로퍼티 관찰자(@objc dynamic var)를 호출하고 값을 업데이트할 수 있습니다.

 

하지만 동적으로 처리되므로 컴파일러가 해당 프로퍼티를 최적화할 수 없다고 하네요.

당연히 성능상 이유로도 남용해서는 안 되겠죠?

 

간단히 KVO(Key-Value Observing) 코드를 봅시다.

// Objective-C에서 처리를 하므로 NSObject를 상속 해줘야 합니다.
class CustomMap: NSObject {
    
    @objc dynamic var coordinate: Double
    
    init(coordinate: Double) {
        self.coordinate = coordinate
    }
}

var map = CustomMap(coordinate: 5)

// coordinate 값이 변할 때 observe handler가 동작 합니다
// options을 통해 사용하고 싶은 기능을 추가하면 된다.
// .old(이전 값), .new(변한 값), .prior(이전/현재 여부), .initial(처음 생성 값)
map.observe(\.coordinate, options: [.old, .new, .initial]) { object, change in
    // isPrior의 값이 true면 이전값을 false면 현재값을 나타냄
    print(object.coordinate, change.isPrior)
    // 이전 값, 변한 현재 값 (만약 이전 값이 없으면 nil)
    print(change.oldValue, change.newValue)
}

// 5.0 false  ---  이전 값(object.coordinate, change.isPrior)
// nil Optional(5.0)  ---  initial 사용 시(change.oldValue, change.newValue)

// 값을 변경 하게 되면
map.coordinate = 10

// 10.0 false  ---  현재 값(object.coordinate, change.isPrior)
// Optional(5.0) Optional(10.0)  ---  change.oldValue, change.newValue

 

 

참고

https://zeddios.tistory.com

https://myssun0325.tistory.com

Key-Value Observing Programming Guide

 

 

 

반응형
profile

Danny의 iOS 컨닝페이퍼

@Danny's iOS

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!