为什么这个初始化被分派到内部扩展的初始化?

huangapple go评论60阅读模式
英文:

Why does this init gets dispatched to an internal extension init?

问题

我在理解这个模块间通信方面遇到了困难。我的应用程序使用3个模块。

我们将它们称为Module A、Module B和Module C。

Module B和Module C都使用Module A。

依赖关系看起来像这样:为什么这个初始化被分派到内部扩展的初始化?

假设我在Module A中声明了这个BaseViewController

open class BaseViewController: UIViewController { }

在Module B中,我有以下声明:

extension BaseViewController {
    internal convenience init() {
        self.init(nibName: String(describing: type(of: self)), bundle: .moduleA)
    }
}

从我的了解来看,这个声明应该只在模块级别可见,而不是在模块外部。

在Module C中,我有一个使用编程布局的BaseViewController子类。

class MySuperViewController: BaseViewController { }

在同一Module C中,我有一个协调器,它像这样初始化这个视图控制器:

let mySuperVC = MySuperViewController()

我调用了这个从我的iOS应用程序中导入所有A、B和C模块的mySuperVC的协调器方法。

我原本期望MySuperViewController()调用UIViewController的初始化程序,因为BaseViewControllerconvenience init声明在Module B中,而且是internal的,然而,实际调用的初始化程序是Module B中的internal初始化程序,因此导致我的应用程序崩溃,因为它找不到nib。

我尝试在Module C中编写一个相同的convenience init,但它调用的仍然是Module B中的相同internal init。为什么会发生这种情况?

英文:

I'm having a hard time understanding this intermodule communication. My app consumes 3 modules.

We will call them Module A, Module B and Module C.

Both Module B and Module C consume Module A.

The dependency looks something like this: 为什么这个初始化被分派到内部扩展的初始化?

Let's say I have this BaseViewController declared in Module A.

open class BaseViewController: UIViewController { }

In module B, I have the following declaration:

extension BaseViewController {
    internal convenience init() {
        self.init(nibName: String(describing: type(of: self)), bundle: .moduleA)
    }
}

Now from my knowledge, this declaration should only be visible at a module level, not outside the module.

In module C, I have a BaseViewController subclass which uses programmatic layout.

class MySuperViewController: BaseViewController { }

In the same module C, I have a coordinator which initialises this view controller like this:

let mySuperVC = MySuperViewController()

I call this coordinator method that instantiates this mySuperVC from my iOS app where I import all A, B and C modules.

I was expecting that MySuperViewController() to call the UIViewController's initialiser, since the convenience init of the BaseViewController is declared inside Module B and it is internal, however, the initialiser that is being called is the internal one from Module B, thus making my app crash since it's not finding the nib.

I tried to write an identical convenience init inside Module C, but it calls the same internal init from Module B. Why is this happening?

答案1

得分: 1

扩展

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

你的扩展无效,调用的方法是未定义的。init()是UIViewController上的现有方法,因此无法在扩展中覆盖它。

实际上为什么会发生这种情况,BaseViewController是一个Objective-C类型。当加载模块时,此init方法将附加到它(当前没有这个方法;只有在其超类上有一个)。当稍后的调用者向BaseViewController实例发送选择器“init”时,它将在层次结构中向上查找此方法。在运行时,系统不知道方法来自哪里。它只是在加载模块时附加。这是不确定性的,这就是为什么在扩展中覆盖方法是未定义行为的原因。

英文:

From Extensions:

>Extensions can add new functionality to a type, but they can’t override existing functionality.

Your extension is invalid and it is undefined what method will be called. init() is an existing method on UIViewController, and so you cannot override it in an extension.

As a practical matter of why this happens, BaseViewController is an Objective-C type. When the module is loaded, this init method is attached to it (there isn't one currently; there's just one on its superclass). When a later caller sends the selector "init" to the BaseViewController instance, it will find this method while walking up the hierarchy. At runtime, the system does not know where the method came from. It just gets attached when the module is loaded. This is non-deterministic, which is why overriding methods in extensions is undefined behavior.

huangapple
  • 本文由 发表于 2023年3月8日 19:17:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75672317.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定