英文:
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
的初始化程序,因为BaseViewController
的convenience 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论