编程iOS 开发者

Swift 中动态方法调用是如何工作的?哪些机制(例如 @objc、dynamic、AnyObject)允许在运行时通过名称调用方法,并在什么情况下这是可能的?请提供使用示例。

用 Hintsage AI 助手通过面试

答案

动态方法调用是指在运行时(runtime)而非编译时按照名称调用方法的能力。在 Swift 中,这可以通过与 Objective-C runtime 的互动来实现,依赖于修饰符 @objcdynamic,或者使用协议 AnyObject

机制:

  • @objc — 标记方法/属性/类以导出到 Objective-C runtime。
  • dynamic — 要求通过 Objective-C 消息派发执行,而不是通过静态调用(vtable)。
  • AnyObject 协议允许将方法声明为可选,并允许通过可选链调用它们。

限制:

  • 仅适用于继承自 NSObject 的类,或标记为 @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!" } }

类似地,可以通过 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 runtime 不兼容,因此它们不能作为动态参与者。


由于对此主题细微差别不熟悉而导致的实际错误示例


故事

在一个项目中,使用了 @objc 协议作为委托。一个开发者移除了委托类继承自 NSObject 的继承,结果可选方法无法通过可选链调用。架构停止工作,测试失败频繁。


故事

在尝试在结构体的属性上使用 KVO(键值观察)的过程中,开发者将其标记为 dynamic。代码编译成功,但未按预期工作,因为结构体不支持 Objective-C runtime。对变化的动态反应没有生效,导致 UI 更新遗漏。


故事

在类的扩展中,忘记为通过 perform(_: ) 调用的函数添加 @objc 修饰符,结果在生产环境中试图调用未关联的选择器时发生崩溃。经过长时间的搜索原因,最终发现缺少函数导出到 Objective-C。