一尘不染

Swift扩展中的重写方法

swift

我倾向于只将必需品(存储的属性,初始化器)放入我的类定义中,然后将其他所有内容移动到它们自己的extension类中,有点像extension我也将分组的每个逻辑块// MARK:

例如,对于一个UIView子类,我将获得与布局相关的东西的扩展,用于订阅和处理事件的扩展,等等。在这些扩展中,我不可避免地必须重写一些UIKit方法,例如layoutSubviews。直到今天,我从未注意到这种方法有任何问题。

以此类的类层次结构为例:

public class C: NSObject {
    public func method() { print("C") }
}

public class B: C {
}
extension B {
    override public func method() { print("B") }
}

public class A: B {
}
extension A {
    override public func method() { print("A") }
}

(A() as A).method()
(A() as B).method()
(A() as C).method()

输出为A B C。这对我来说毫无意义。我读到了协议扩展是静态分配的,但这不是协议。这是一个常规类,我希望方法调用在运行时动态分派。显然,调用C至少应该动态调度并产生C

如果我从中删除继承NSObject并创建C一个根类,则编译器会抱怨说declarations in extensions cannot override yet,这是我已经读过的。但是NSObject,以根类为基础如何改变事物呢?

将这两个重写都A A A按预期的那样移入类声明产生,仅移动B‘s产生A B B,仅移动A‘s产生C B C,对我来说,最后一个绝对没有意义:甚至没有静态类型的A产生A-output了!

dynamic关键字添加到定义或替代中似乎确实给了我’从类层次结构中的那一点向下’的所需行为…

让我们将示例更改为稍微有些结构化的东西,实际上是什么使我发布了这个问题:

public class B: UIView {
}
extension B {
    override public func layoutSubviews() { print("B") }
}

public class A: B {
}
extension A {
    override public func layoutSubviews() { print("A") }
}


(A() as A).layoutSubviews()
(A() as B).layoutSubviews()
(A() as UIView).layoutSubviews()

现在我们得到了A B A。在这里,我无法以任何方式使UIView的layoutSubviews动态化。

将这两个重写都移到类声明中会使我们A A A再次获得成功,只有A或B仍会获得我们A B Adynamic再次解决了我的问题。

从理论上讲,我可以添加我曾经做过的dynamic所有override事情,但是我觉得自己在这里做错了其他事情。

extension像我一样使用s对代码进行分组真的错误吗?


阅读 1502

收藏
2020-07-07

共1个答案

一尘不染

扩展不能/不应覆盖。

如Apple的Swift指南中所述,无法覆盖扩展中的功能(如属性或方法)。

扩展可以为类型添加新功能,但不能覆盖现有功能。

Swift开发人员指南

编译器允许您重写扩展名以与Objective-C兼容。 但这实际上违反了语言指令。

just这让我想起了艾萨克·阿西莫夫(Isaac Asimov)的“ 机器人三定律
”🤖

扩展( 语法糖
)定义了接收自己参数的独立方法。需要调用的函数,即layoutSubviews取决于编译器知道何时编译代码的上下文。UIView继承自UIResponder,而UIResponder继承自NSObject,
因此允许扩展名中的替代,但不应重写

因此,分组没有错,但是您应该在类中而不在扩展中覆盖。

指令说明

如果该方法与Objective-C兼容,则只能override使用超类方法,即load() initialize()在子类的扩展中。

因此,我们可以了解一下为什么它允许您使用进行编译layoutSubviews

除了使用纯Swift框架(允许纯Swift运行时)外,所有Swift应用都在Objective-C运行时内执行。

我们发现,Objective-
C运行时通常会调用两个类的main方法,load()initialize()在初始化应用程序进程中的类时自动调用它们。

关于dynamic修饰符

Apple开发人员库
(archive.org

您可以使用dynamic修饰符要求通过Objective-C运行时动态调度对成员的访问。

通过Objective-C运行时导入Swift API时,不能保证动态分配属性,方法,下标或初始化程序。
Swift编译器仍然可以绕过Objective-C运行时来取消虚拟化或内联成员访问以优化代码的性能。 😳

因此,dynamic可以将其应用于layoutSubviews->,UIView Class因为它由Objective-
C表示,并且始终使用Objective-C运行时来访问该成员。

这就是为什么编译器允许您使用override和的原因dynamic

2020-07-07