无法覆盖另一个模块swift中类扩展中声明的open方法

Myk*_*yuk 6 ios swift swift3

我在另一个模块(使用pod)中声明了类扩展.

public extension UIView {
  open func doStuff() {...}
}
Run Code Online (Sandbox Code Playgroud)

当我尝试在当前项目模块中的子类中重写此方法时

class ConcreteView : UIView {
  override open func doStuff() {...}
}
Run Code Online (Sandbox Code Playgroud)

我收到一个错误:

覆盖其定义模块之外的非打开实例方法

尽管该方法实际上标记为开放

作为一种解决方法,我在同一模块中声明了另一个类,其中声明了扩展并在那里覆盖了所需的方法

public class CustomView: UIView {
  override open func doStuff() {...}
}
Run Code Online (Sandbox Code Playgroud)

并在主模块中将此类设置为我的类的超类

class ConcreteView : CustomView
Run Code Online (Sandbox Code Playgroud)

所以只有在此之后我才能覆盖该方法.

它看起来真的像swift3中的一个错误,但也许我已经省略了为什么它以这种方式工作的一些理解?

Mar*_*n R 13

简答:这个doStuff方法

public extension UIView {
  open func doStuff() {...}
}
Run Code Online (Sandbox Code Playgroud)

具有有效的访问级别"公共",因为扩展名标记为公共.因此,它不能在子类中重写.

请注意,Xcode警告

警告:在PUBLIC扩展中声明实例方法

并且警告文本应该是(见下文)

警告:在PUBLIC扩展中声明OPEN实例方法

要解决此问题,请删除public扩展的访问修饰符:

extension UIView {
  open func doStuff() {...}
}
Run Code Online (Sandbox Code Playgroud)

现在,扩展的访问级别为"open"(继承自open class UIView),有效访问级别doStuff为"open",可以进行子类化.

更长的回答:

Swift参考中的访问控制说明了这一点

...您可以使用显式访问级别修饰符标记扩展名...为​​扩展名中定义的所有成员设置新的默认访问级别.仍可以在单个类型成员的扩展中覆盖此新默认值.

但实际上,您只能将扩展中的类型成员限制为相同或较低的访问权限.不幸的是,我在文档中找不到这个事实的明确参考.

SE-0117允许区分公共访问和公共覆盖 状态

例如,类型成员的真实访问级别计算为类型的真实访问级别和成员的声明访问级别的最小值.如果该类是公共的但成员是开放的,则真正的访问级别是公共的.

但没有解释这如何适用于扩展.

检查可以在编译器源代码 TypeCheckAttr.cpp中看到.如果项目的访问级别大于包含扩展的访问级别, diag::access_control_ext_member_more 则会发出诊断消息:

WARNING(access_control_ext_member_more,none,
    "declaring %select{PRIVATE|a fileprivate|an internal|a public}0 %1 in "
    "%select{a private|a fileprivate|an internal|PUBLIC}2 extension",
    (Accessibility, DescriptiveDeclKind, Accessibility))
Run Code Online (Sandbox Code Playgroud)

请注意,选择中缺少"打开"级别,这就是它缺失的原因

警告:在PUBLIC扩展中声明实例方法