Инициализация удобства подкласса UINavigationController делает подклассом константу init дважды

У меня есть подклассы UINavigationController и UITableViewController.

Для инициализации подклассов я решил использовать некоторые convenience init методы convenience init которые вызывают некоторый назначенный инициализатор суперкласса. Кроме того, каждый подкласс некоторые let постоянно:

let someValue: SomeClass = SomeClass()

Каждый класс успешно инициализируется путем вызова его вновь созданного метода convenience init.

Проблема в том, что константа let инициализируется TWICE в подклассе UINavigationController.

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())

Можем ли мы использовать convenience init как указано выше? Зачем?

Почему в этих двух случаях поведение convenience init отличается?

Проверено: версия 8.2 beta (8C30a), версия 8.2 (8C38), версия 8.2.1 (8C1002)

PS Игровой журнал приведенный выше код:

"Constant Initialization -> Navigation"
"Constant Initialization -> Navigation"
"Constant Initialization -> Table"

Ответы

Ответ 1

Так что это кажется странным "ожидаемым поведением" со Свифт. Причина, по которой это происходит, связана с постоянной инициализированной service недвижимости. А именно, этот пост суммирует проблему, которую вы видите довольно хорошо: сообщение в блоге

По сути, супер-классы, основанные на Obj-C, вызывают утечку памяти, и ваше свойство service инициализируется дважды из-за этого шаблона инициализации Obj-C:

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

Простым решением, чтобы избежать этого, является следующий шаблон:

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)
    }
}

Все, что меняется от вашей реализации, инициализирует service только после инициализации самого класса.

Другое решение, однако, состоит в том, чтобы использовать назначенный инициализатор для суперкласса вашей инициализации. Это означает, что вы не используете инициализатор удобства, который использовал бы описанную выше схему инициализации Obj-C.