覆盖Swift扩展中的方法

Chr*_*orr 119 swift swift-extensions swift2

我倾向于只将必需品(存储属性,初始化器)放入我的类定义中,并将其他所有内容移动到自己的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()
Run Code Online (Sandbox Code Playgroud)

输出是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()
Run Code Online (Sandbox Code Playgroud)

我们现在得到A B A.在这里,我不能以任何方式使UIView的layoutSubviews动态化.

将两个覆盖移动到他们的类声明中A A A再次获得我们,只有A或者只有B仍然可以获得我们A B A.dynamic再次解决了我的问题.

理论上我可以添加dynamicoverride我做过的所有事情,但我觉得我在这里做了别的错事.

使用extensions像我一样分组代码是不是真的错了?

tym*_*mac 201

扩展不能/不应该覆盖.

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

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

Apple开发人员指南

编译器允许您在扩展中覆盖以与Objective-C兼容.但实际上它违反了语言指令.

这让我想起艾萨克·阿西莫夫的" 机器人三定律 "

扩展(语法糖)定义接收自己的参数的独立方法.调用的函数layoutSubviews取决于编译器在编译代码时所知道的上下文.UIView继承自UIRbject的继承自USObject的UIResponder,因此扩展中的覆盖是允许的,但不应该.

所以分组没有错,但是你应该在类中覆盖而不是在扩展中.

指令说明

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

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

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

当我们发现了Objective-C的运行时间一般调用两个类主要方法load()initialize()初始化在应用程序的流程类时自动.

关于dynamic修饰语

来自iOS开发人员库

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

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

所以dynamic可以应用于你的layoutSubviews- > UIView Class因为它由Objective-C表示,并且总是使用Objective-C运行时访问该成员.

这就是编译器允许你使用override和的原因dynamic.

  • 扩展不能仅覆盖类中定义的方法.它可以覆盖父类中定义的方法. (4认同)
  • @AuRis你有什么参考吗? (3认同)
  • 太令人沮丧了,所以基本上当你想用项目中已有的代码创建一个框架时,你将不得不子类化并重命名所有内容...... (2认同)

Wai*_*ain 17

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

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

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

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


Ala*_* T. 9

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

如果确保在单独的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)" }
 }
Run Code Online (Sandbox Code Playgroud)

...

 // ---------- 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))" 
    }
 }
Run Code Online (Sandbox Code Playgroud)

...

 // ---------- 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))" 
    }
 }
Run Code Online (Sandbox Code Playgroud)

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

正如您所看到的,继承(使用变量名称)可以使用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"
Run Code Online (Sandbox Code Playgroud)

  • 我想这对我自己的方法有效,但在我的类中覆盖System Framework方法时则不行. (2认同)

Win*_*ter 5

使用 POP(面向协议的编程)来覆盖扩展中的函数。

protocol AProtocol {
    func aFunction()
}

extension AProtocol {
    func aFunction() {
        print("empty")
    }
}

class AClass: AProtocol {

}

extension AClass {
    func aFunction() {
        print("not empty")
    }
}

let cls = AClass()
cls.aFunction()
Run Code Online (Sandbox Code Playgroud)