为了账号安全,请及时绑定邮箱和手机立即绑定

覆盖Swift扩展中的方法

覆盖Swift扩展中的方法

慕神8447489 2019-08-12 10:19:44
覆盖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产生A B B,仅移动A产生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 A。dynamic再次解决了我的问题。理论上我可以添加dynamic到override我做过的所有事情,但我觉得我在这里做了别的错事。使用extensions像我一样分组代码是不是真的错了?
查看完整描述

3 回答

?
慕标5832272

TA贡献1966条经验 获得超4个赞

Swift的目标之一是静态调度,或者更确切地说是动态调度的减少。然而,Obj-C是一种非常动态的语言。您所看到的情况来自于两种语言之间的联系以及它们协同工作的方式。它不应该真正编译。

关于扩展的一个要点是它们用于扩展,而不是用于替换/覆盖。从名称和文档中可以清楚地看出这是意图。实际上,如果从代码中取出Obj-C的链接(NSObject作为超类删除),它将无法编译。

因此,编译器正在尝试确定它可以静态调度的内容以及动态分派的内容,并且由于代码中的Obj-C链接,它正在陷入差距。dynamic“工作” 的原因是因为它迫使Obj-C连接所有东西,所以它总是动态的。

因此,使用扩展进行分组并没有错,这很好,但是在扩展中覆盖它是错误的。任何覆盖都应该在主类本身中,并调用扩展点。


查看完整回答
反对 回复 2019-08-12
?
叮当猫咪

TA贡献1776条经验 获得超12个赞

有一种方法可以实现类签名和实现(在扩展中)的清晰分离,同时保持在子类中具有覆盖的能力。诀窍是使用变量代替函数

如果确保在单独的swift源文件中定义每个子类,则可以使用计算变量进行覆盖,同时保持相应的实现在扩展中干净地组织。这将绕过Swift的“规则”,并使您的课程的API /签名整齐地组织在一个地方:

 // ---------- BaseClass.swift -------------
 public class BaseClass
 {
     public var method1:(Int) -> String { return doMethod1 }

     public init() {}
 }

 // the extension could also be in a separate file  
 extension BaseClass
 {    
     private func doMethod1(param:Int) -> String { return "BaseClass \(param)" }
 }

...

 // ---------- ClassA.swift ----------
 public class A:BaseClass
 {
    override public var method1:(Int) -> String { return doMethod1 }
 }

 // this extension can be in a separate file but not in the same // file as the BaseClass extension that defines its doMethod1 implementation extension A {
    private func doMethod1(param:Int) -> String 
    { 
       return "A \(param) added to \(super.method1(param))" 
    }
 }

...

 // ---------- ClassB.swift ---------- public class B:A {
    override public var method1:(Int) -> String { return doMethod1 }
 }

 extension B {
    private func doMethod1(param:Int) -> String 
    { 
       return "B \(param) added to \(super.method1(param))" 
    }
 }

每个类的扩展都能够为实现使用相同的方法名称,因为它们是私有的并且彼此不可见(只要它们在单独的文件中)。

正如您所看到的,继承(使用变量名称)可以使用super.variablename正常工作

 BaseClass().method1(123)         --> "BaseClass 123"
 A().method1(123)                 --> "A 123 added to BaseClass 123"
 B().method1(123)                 --> "B 123 added to A 123 added to BaseClass 123"
 (B() as A).method1(123)          --> "B 123 added to A 123 added to BaseClass 123"
 (B() as BaseClass).method1(123)  --> "B 123 added to A 123 added to BaseClass 123"


查看完整回答
反对 回复 2019-08-12
  • 3 回答
  • 0 关注
  • 650 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信