我倾向于只把必要的东西(存储属性、初始值设定项)放入我的类定义中,并将其他所有东西放入它们自己的extension中,有点像每个逻辑块extension,我也会将其分组为// MARK:.

例如,对于一个UIView子类,我最终会得到一个与布局相关的扩展,一个用于订阅和处理事件等.在这些扩展中,我不可避免地要覆盖一些UIKit方法,例如layoutSubviews.直到今天,我才注意到这种方法有任何问题.

以这个类层次 struct 为例:

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个会产生A B B个,只移动A个会产生C B C个,最后一个对我来说毫无意义:即使是静态键入A的一个也不会再产生A个输出了!

dynamic关键字添加到定义或重写中,似乎确实会"从类层次 struct 的那个点向下"提供所需的行为...

让我们把我们的例子换成一个不那么复杂的例子,到底是什么让我发布了这个问题:

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的布局子视图动态化.

将这两个重写移到它们的类声明中,我们再次得到A A A,只有A或B仍然得到A B A.dynamic再次解决了我的问题.

理论上,我可以在我做的所有override项中加dynamic项,但我觉得我在做别的错事.

像我这样用extension来分组代码真的不对吗?

推荐答案

扩展不能/不应该覆盖.

正如苹果公司的Swift指南所述,不可能覆盖扩展中的功能(如属性或方法).

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

Swift Developer Guide

The compiler is allowing you to override in the extension for compatibility with Objective-C. But it's actually violating the language directive.

?这让我想起了艾萨克·阿西莫夫的《Three Laws of Robotics》?

扩展(syntactic sugar)定义了接收自己参数的独立方法.调用的函数即layoutSubviews取决于编译器在编译代码时知道的上下文.UIView继承自UIResponder,后者继承自NSObject so the override in the extension is permitted but should not be.

所以分组没有什么错,但应该在类中重写,而不是在扩展中重写.

Directive Notes

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

因此,我们可以看看为什么它允许您使用layoutSubviews进行编译.

所有Swift应用程序都在Objective-C运行时内执行,但使用纯Swift纯框架时除外,纯Swift纯框架允许Swift纯运行时.

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

Regarding the dynamic modifier

Apple Developer Library(archive.org)

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

当Objective-C运行时导入Swift API时,无法保证对属性、方法、子脚本或初始值设定项进行动态调度.The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime.?

So dynamic can be applied to your layoutSubviews -> UIView Class since it’s represented by Objective-C and access to that member is always used using the Objective-C runtime.

这就是为什么编译器允许你使用overridedynamic.

Swift相关问答推荐

如何根据列表中的项目数量创建多个变量?

通过SwiftUI中的列表 Select 从字典中检索值

SwiftUI map 旋转

按SWIFT中每个ID的最大值进行筛选/排序

为什么ClosedRange<;Int&>包含的速度比预期慢340万倍?

SwiftUI,如何更改具有多个按钮和一个布尔条件的一个按钮标签

在 Swift 5.7 中使用协议作为类型时什么时候需要写 `any`

将 ArgumentParser 与 Swift 并发一起使用

Swift Random Float Fatal Error:在无限范围内没有均匀分布

ScreenCaptureKit/CVPixelBuffer 格式产生意外结果

如何在类中打印函数

关于变量的视图的 SwiftUI 生命周期

SwiftUI:异步网络请求后@State 值不更新

SwiftUI Preview 不适用于 Core Data

swiftui更改显示的 Select 器值

如何让 UIKit 挤压你的视图,使其适合屏幕

理解 Swift 2.2 Select 器语法 - #selector()

从 NSData 对象在 Swift 中创建一个数组

如何在今天的扩展(iOS)中访问 CoreData 模型

使用 DispatchTime.now() + float 延迟?