Как создать экземпляр слабого делегата без запуска «Экземпляр будет немедленно освобожден, потому что свойство «tableViewDelegate» является «слабым»»

#swift #delegates #weak

#swift #делегирует #слабый

Вопрос:

Я пытаюсь выделить источник данных моего TableView в отдельный объект делегата. Поскольку этому делегату в какой-то момент необходимо получить доступ к tableview, мне нужна ссылка на делегирующий объект в делегате; и поскольку оба являются классами, мне нужно избежать сильных циклов ссылок, создав делегат weak

Для достижения этого я попробовал следующий код.

 class MyViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    weak var tableViewDelegate: UITableViewDataSource?

    override func viewDidLoad() {
        super.viewDidLoad()
        tableViewDelegate = TableViewDelegate() // throwing a warning
        tableView.dataSource = tableViewDelegate
    }
}
  

Когда я пытаюсь создать экземпляр делегата, Xcode выдает предупреждение: «Экземпляр будет немедленно освобожден, потому что свойство ‘tableViewDelegate’ является ‘слабым'»

Итак, чтобы исправить это, я делаю следующее:

 class MyViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    weak var tableViewDelegate: UITableViewDataSource?

    override func viewDidLoad() {
        super.viewDidLoad()
        let delegate = TableViewDelegate() // worried this creates a strong reference.
        self.tableViewDelegate = delegate
        tableView.dataSource = delegate
    }
}
  

Пожалуйста, подтвердите, верно ли следующее или нет: инициализируя делегат в методе viewDidLoad(), мне не грозит создание надежной ссылки, потому что переменная, содержащая этот экземпляр, освобождается, как только мы покидаем область действия этого метода. Или, другими словами: единственный раз, когда нам нужно беспокоиться о том, что переменная (которая указывает на класс) создает сильную ссылку, — это если переменная инициализирована на уровне класса и, следовательно, будет существовать столько же, сколько и класс.

Это правильно?

Ответ №1:

Пожалуйста, подтвердите, верно ли следующее или нет: инициализируя делегат в методе viewDidLoad(), мне не грозит создание надежной ссылки, потому что переменная, содержащая этот экземпляр, освобождается, как только мы покидаем область действия этого метода.

Правильно. Сильная ссылка исчезает, как только завершается область, в которой let объявлено.

К сожалению, это означает, что ваш делегат все равно будет освобожден. Все, что вы сделали, это отключили предупреждение.

В принципе, вам нужно где-то иметь сильную ссылку на делегат, иначе он сразу исчезнет. По моим ощущениям, вы должны сделать ссылку в MyViewController strong . Цикла строгой ссылки не будет, пока ваш делегат не содержит строгой ссылки на контроллер представления. Если вам нужна ссылка на MyViewController в делегате, сделайте ее слабой, т. Е. делегатом владеет контроллер представления, а не делегат — контроллер представления.


Ответ на комментарий ниже:

почти во всех руководствах, которые я нашел, свойство delegate указано как слабое, так что это кажется стандартной практикой.

Да, это довольно стандартная практика, есть исключения, в том числе в Cocoa. Однако стандартной практикой является наличие слабой ссылки на делегат в делегирующем объекте. В вашем случае делегирующим объектом является thew, UITableView а не the MyViewController . В вашем первом примере из Интернета FileImporter аналогично UITableView в вашем коде. Во втором примере DetailViewController является делегирующим объектом.

Если вы подумаете об этом, ваше TableViewDelegate используется вместо приведения MyViewController в соответствие с протоколом. Имеет абсолютный смысл, что MyViewController будет владеть делегатом.

Комментарии:

1. Да, вы правы: мой делегат все еще был освобожден. И ваше предлагаемое решение имеет смысл для меня, но почти во всех руководствах, которые я нашел, свойство delegate указано как слабое, так что это кажется стандартной практикой. Вот два примера: 1) swiftbysundell.com/posts/delegation-in-swift , 2) useyourloaf.com/blog/… Итак, если стандартная практика диктует, что делегат должен быть слабым, как мне безопасно инициализировать его, не вызывая предупреждения о том, что он будет освобожден.

2. Учтите также, что обычно сам контроллер представления является делегатом и источником данных содержащегося в нем табличного представления. Это не создает никаких проблем, поскольку UITableView определяет свои свойства делегата и источника данных как слабые.

3. @thecloud_of_unknowing Вам нужно более внимательно ознакомиться с руководствами. Ссылка на делегат в делегирующем объекте обычно слабая (бывают исключения). В вашем случае это UITableView . Где-то должна быть хотя бы одна сильная ссылка, иначе она просто исчезнет, когда вы этого не захотите.

4. @JeremyP Блестяще! Спасибо. Я упустил тот факт, что представление таблицы (а не контроллер представления) является делегирующим объектом и определяет его делегат как слабый.

Ответ №2:

Вот как я решил эту проблему:

     let dataSource = MyDataSource()

    lazy var viewModel : MyViewModel = {
        let viewModel = MyViewModel(dataSource: dataSource)
        return viewModel
    }()
  

а затем в viewDidLoad():

     tableView.delegate = self
    tableView.dataSource = dataSource
  

Вы можете увидеть полный демонстрационный проект здесь