Путаница в отношении того, как использовать списки захвата, чтобы избежать цикла ссылок

Мой пользовательский подкласс UIViewController имеет сохраненное свойство закрытия. Сигнатура закрытия определяется как принимающая один аргумент того же типа класса:

class MyViewController {

    var completionHandler : ((MyViewController)->(Void))?

    // ...
}

... идея в том, что объект передает себя обратно в качестве аргумента обработчика, немного как инициализатор UIAlertAction.

Кроме того, для удобства у меня есть фабричный (-ish) метод класса:

class func presentInstance(withCompletionHandler handler:((MyViewController)->(Void)))
{
   // ...
}

...который выполняет следующие действия:

  1. Создает экземпляр контроллера представления,
  2. Назначает обработчик завершения свойству,
  3. Представляет его модально из того, что во время вызова является контроллером верхнего/корневого представления.

Мой контроллер представления определенно дает утечку: я установил точку останова на deinit(), но выполнение никогда не достигает ее, даже после того, как я закончил работу с моим контроллером представления, и он был отклонен.

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

  1. Где я объявляю свойство закрытия? (как?)

    var completionHandler : ((MyViewController)->(Void))?
    // If so, where does it go?
    
  2. Где я объявляю параметр закрытия?

    class func presentInstance(withCompletionHandler handler:((MyViewController)->(Void)))
    {
    // Again, where could it go?
    
  3. Где я вызываю вышеуказанную функцию и передаю тело закрытия?

    MyViewController.presentInstance(withCompletionHandler:{
        [unowned viewController] viewController in 
    
        // ...
    })
    // ^ Use of unresolved identifier viewController
    // ^ Definition conflicts with previous value
    
  4. Где я на самом деле вызываю закрытие, в сторону self? Ни один из них не будет компилироваться:

    self.completionHandler?(unowned self)
    self.completionHandler?([unowned self] self)
    self.completionHandler?([unowned self], self)
    

person Nicolas Miari    schedule 25.04.2016    source источник


Ответы (1)


Что ж, оказывается, мой контроллер представления удерживался блоком, но не тем, о котором я думал:

class MyViewController
{
    deinit(){
        print("Deinit...")
    }

    // ...

    @IBAction func cancel(sender:UIButton)
    {
        completionHandler(self) 
        // View controller is dismissed, AND
        // deinit() gets called.     
    }

    @IBAction func punchIt(sender: UIButton)
    {
        MyWebService.sharedInstance.sendRequest(    
            completion:{ error:NSError? in

                 self.completionHandler(self)  
                 // View controller is dismissed, BUT
                 // deinit() does NOT get called. 
            }
        )
    }

... так что это закрытие, переданное MyWebService.sharedInstance.sendRequest(), поддерживало мой контроллер представления. Я исправил это, добавив список захвата следующим образом:

MyWebService.sharedInstance.sendRequest(    
            completion:{ [unowned self] error:NSError? in

Однако я до сих пор не совсем понимаю, почему недолговечный обработчик завершения, переданный классу веб-сервиса, выполненный один раз и удаленный, поддерживал работоспособность моего контроллера представления. Это замыкание, нигде не хранящееся как свойство, должно быть освобождено сразу после выхода, верно?

Я должен что-то упустить. Думаю, я еще не до конца разобрался с закрытием portals.

person Nicolas Miari    schedule 25.04.2016