동적 메소드 호출은 컴파일 시점이 아닌 실행 시점(runtime)에 이름으로 메소드를 호출할 수 있는 기능입니다. Swift에서는 @objc와 dynamic 수정자 또는 AnyObject 프로토콜을 통해 Objective-C 런타임과 상호작용하여 이 기능을 구현할 수 있습니다.
메커니즘:
@objc — 메소드/속성/클래스를 Objective-C 런타임에 내보내기 위해 표시합니다.dynamic — 정적 호출(vtable)을 사용하지 않고 Objective-C 메시지 디스패치를 통해 실행되도록 요구합니다.제한 사항:
@objc로 표시된 프로토콜과만 작동합니다.@objc 속성이 있거나 NSObject에서 상속받은 것으로 표시된 메소드/속성에만 해당합니다.예시:
import Foundation class Person: NSObject { @objc dynamic func sayHello() -> String { return "Hello!" } } let person = Person() // Selector (perform:)를 통한 동적 호출: if person.responds(to: #selector(Person.sayHello)) { if let result = person.perform(#selector(Person.sayHello))?.takeUnretainedValue() as? String { print(result) // "Hello!" } } }
비슷하게 Optional 체인을 통해 AnyObject로 기능을 호출할 수 있습니다:
@objc protocol Greeter { @objc optional func greet() } class Robot: NSObject, Greeter { func greet() { print("Beep-beep!") } } let anyGreeter: AnyObject = Robot() anyGreeter.greet?() // "Beep-beep!"
구조체가 @objc, dynamic 또는 AnyObject를 통해 동적 메소드 호출을 지원할 수 있습니까?
답변: 아니요, NSObject에서 상속된 클래스 또는 @objc 수정자가 있는 클래스/프로토콜만 그러한 메커니즘을 지원할 수 있습니다. 구조체와 열거형은 Objective-C 런타임과 호환되지 않아 동적 구성원이 될 수 없습니다.
이야기
프로젝트에서 델리게이트를 위해 @objc 프로토콜을 사용했습니다. 한 개발자가 델리게이트 클래스를 NSObject에서 상속받지 않도록 변경하여 optional 메소드가 선택적 체인을 통해 호출되지 않게 되었습니다. 아키텍처가 작동하지 않게 되었고, 테스트가 계속 실패했습니다.
이야기
구조체의 속성에서 KVO(키-값 관찰)를 사용하려고 시도한 개발자가 해당 속성을 dynamic으로 표시했습니다. 코드는 컴파일되었지만 기대한 대로 작동하지 않았습니다. 구조체는 Objective-C 런타임을 지원하지 않기 때문입니다. 변화에 대한 동적 반응이 작동하지 않아 UI 업데이트를 놓쳤습니다.
이야기
클래스의 익스텐션에서 수행(perform(_:))를 통해 호출된 함수에 @objc 수정자를 추가하는 것을 잊었습니다. 그 결과, 프로덕션에서 관련 없는 선택자를 호출하려 할 때 크래시가 발생했습니다. 원인은 오랜 시간 동안 문제가 되어, 함수의 Objective-C 내보내기를 잊은 것을 발견할 때까지 시간이 걸렸습니다.