@objc关键字扩展子类行为

13

有人能解释为什么在这里需要使用@objc关键字才能编译代码吗?

据我了解,这个关键字是为了使ObjC消息方法分派正常工作。但这不是一个NSObject实例。

 class MyClass {
 }

 extension MyClass {
     @objc func extensionMethod() { /// THIS LINE
         print("A")
     }
 }

 class SubClass: MyClass {
     override func extensionMethod() {
         print("B")
     }
 }

@objc关键字是否像dynamic一样启用消息分发?还是不启用?

1个回答

17

@objc关键字是否可以激活消息派发和动态特性?

通常情况下,仅使用@objc属性就可以将给定的类成员公开给Objective-C - Swift仍然可以使用表格或静态分派调用它。如果希望Swift在调用时使用消息派发,则需要将成员标记为dynamic

但是,对于非最终的@objc扩展成员,Swift将自动推断其为dynamic。为什么?因为为了实现互操作性,Swift允许@objc扩展成员进行覆盖和被覆盖(就像在子类类别中重写Obj-C方法一样)。为了实现这种行为,Swift依赖于Obj-C消息派发。

因此,在扩展中,@objc推断为dynamic。您无法重写扩展成员而不将其暴露给Obj-C运行时,因为Swift vtables当前不能在运行时动态地向其中添加成员,而扩展成员目前无法添加到Swift类vtables中。

但这不是一个NSObject实例。

在Apple平台(即与Obj-C互操作性相关的平台上),所有Swift类都会暴露给Obj-C运行时,并且都隐式继承自一个称为_SwiftObject的特殊Obj-C基类,该基类符合NSObjectProtocol。因此,Swift类能够利用消息派发,而无需继承自NSObject


抱歉打扰了。我已经阅读了这篇文章,但仍不确定我的实现方式(在此处:https://stackoverflow.com/questions/50727968/how-to-modify-uibuttons-accessibilitylabel-in-an-extension/50820671#50820671)是否能够100%正常工作。我的问题是关于**Swift 3**的。您能否确认一下,如果我添加@objc open override var accessibilityLabel: String?{...,它是否可以毫无疑问地工作?! - mfaani
1
@Honey 不用客气,随时欢迎提问 :) 我不建议在 UIButton 扩展中定义 @objc open override var accessibilityLabel: String?,因为如果 UIButton 添加了自己的 accessibilityLabel 覆盖,就不能保证你的实现会被调用(就像在 Obj-C 类的两个类别中定义相同方法一样,无法保证哪个实现会被调用)。只有在可以保证给定类有一个实现的情况下才是可靠的(例如在 OP 的示例中,他们正在子类中进行覆盖)。 - Hamish
谢谢。那么在Swift4中,这个逻辑的哪一部分发生了变化呢? - mfaani
2
据我所知,Swift 4 中唯一的变化是 Swift 不再对继承自 NSObject 的成员隐式推断 @objc 属性。因此,如果 MyClass 继承自 NSObject(编译器会推断出 @objc),OP 的示例在 Swift 3 中将会编译通过而不需要显式指定 @objc。但现在,无论是否继承自 NSObject,编译器都要求显式指定 @objc,以明确你正在将其暴露给 Obj-C。 - Hamish
这是一个老问题,但是如果你在重写方法中添加关键字 final,那么这是否会使其成为静态分派? - David James
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接