UINavigationController子类的便捷初始化使子类常量初始化两次

iWh*_*Buy 15 uinavigationcontroller ios swift

我有UINavigationController和的子类UITableViewController.

为了初始化子类,我决定使用一些convenience init调用超类的指定初始化器的方法.此外,每个子类都有一些let常量:

let someValue: SomeClass = SomeClass()
Run Code Online (Sandbox Code Playgroud)

通过调用其新创建的convenience init方法成功初始化每个类.

问题是let常量在子类中初始化为TWICEUINavigationController.

import UIKit
import PlaygroundSupport

final class Navigation: UINavigationController {
    convenience init(anyObject: Any) {
        self.init(rootViewController: UIViewController())
    }
    let service = Constant("Constant Initialization -> Navigation")
}

final class Table: UITableViewController {
    convenience init(anyObject: Any) {
        self.init(style: .plain)
    }
    let service = Constant("Constant Initialization -> Table")
}

class Constant: NSObject {
    init(_ string: String) {
        super.init()
        debugPrint(string)
    }
}

Navigation(anyObject: NSNull())
Table(anyObject: NSNull())
Run Code Online (Sandbox Code Playgroud)

我们可以convenience init像上面一样使用吗?为什么?

为什么convenience init这两种情况的行为不同?

检查:版本8.2 beta(8C30a),版本8.2(8C38),版本8.2.1(8C1002)

以上代码的PS Playground日志:

"Constant Initialization -> Navigation"
"Constant Initialization -> Navigation"
"Constant Initialization -> Table"
Run Code Online (Sandbox Code Playgroud)

BHe*_*cks 4

所以这对于 Swift 来说似乎是奇怪的“预期行为”。发生这种情况的原因是由于常量初始化属性service。也就是说,这篇文章很好地总结了您所看到的问题:博客文章

本质上,Obj-C 底层超类正在泄漏内存,并且service由于 Obj-C 初始化的这种模式,您的属性被初始化两次:

- (id)init {
  self = [super init];
  if (self) {
    self = [[self.class alloc] initWithNibName:nil bundle:nil];
  }
  return self;
}
Run Code Online (Sandbox Code Playgroud)

避免这种情况的一个简单解决方案是以下模式:

import UIKit

final class NavigationController: UINavigationController {
    var service: ObjectTest?

    convenience init() {
        self.init(rootViewController: UIViewController())
        self.service = ObjectTest("init nav")
    }
}
class ObjectTest: NSObject{
    init(_ string: String) {
        super.init()
        print(string)
    }
}
Run Code Online (Sandbox Code Playgroud)

您的实现所发生的所有变化只是service在您的类本身初始化之后才进行初始化。

不过,另一种解决方案是对要初始化的超类使用指定的初始化程序。这意味着您不使用使用上述 Obj-C 初始化模式的便利初始化程序。