5

Может кто-нибудь объяснить, почему это не просачивается?Swift - Ожидание утечки после сильного захвата себя в закрытии

Я захватывая self в closure, так что я бы две сильные указатели, указывающие друг на друга, поэтому deinit сообщение должно никогда не вызывается для объекта Person.

Во-первых, это мой класс Person:

class Person { 
    var name: String 
    init(name: String) { self.name = name } 
    deinit { print("\(name) is being deinitialized") } 
} 

И это реализация моего ViewController в:

class ViewController: UIViewController { 

    var john:Person? 

    func callClosureFunction(closure:(name:Bool) ->()) { 
     closure(name: true) 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     john = Person(name:"John") 

     self.callClosureFunction { (name) in 

      self.john?.name = "John Appleseed" 
      self.john = nil 

      // xcode prints - John Appleseed is being deinitialized 
     } 

    } 

} 

я ожидал, чтобы быть в состоянии решить эту проблему, выполнив:

self.callClosureFunction { [weak self] (name) in ... 

Но это было даже не нужно. Зачем?

ответ

3

Поскольку ваш контроллер зрения не является , удерживая закрытием, нет круглого эталона. Если вы написали это:

class ViewController: UIViewController { 

    var john:Person? 
    var closure:(Bool)->()? 

    func callClosureFunction(closure:((name:Bool) ->())?) { 
     closure?(name: true) 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     john = Person(name:"John") 
     closure = { (name) in 

      self.john?.name = "John Appleseed"  

      // Because this closure will never be released, the instance of Person will never deinit either 
     } 
     self.callClosureFunction(closure) 
    } 
} 

то контроллер представления сохранит замыкание и замыкание сохранит контроллер с помощью его ссылки на self. Поэтому ни один из них не будет выпущен, и если вы явно не установите self.john = nil (что вы сделали в своем оригинальном примере), то экземпляр Person никогда не получит deninit.

Общепринято для ненадлежащего использования слабых self в закрытии, когда это не необходимо (и это может привести к некоторым неясным ошибкам). Главное правило, которое следует помнить, состоит в том, что слабые ссылки не являются стандартными по умолчанию в ARC. Сильный должен быть по умолчанию , если только это не приведет к циклу удержания, и в этом случае слабый должен использоваться только для того, чтобы разбить эту циркулярную ссылку. То же самое для замыканий: сильный self должен быть по умолчанию, , еслиself в этом случае также имеет сильную ссылку на само закрытие.

+1

объект 'John Appleseed'' Person' будет по-прежнему деинизировать. См. Мой ответ – Alexander

+0

@AlexanderMomchliov Если закрытие сохраняет VC (self), а VC сохраняет замыкание, как в моем примере, тогда VC никогда не освобождается (и не закрывается). Если VC никогда не деинизирует, то он, в свою очередь, не выпускает свою ссылку на объект Person, а это означает, что объект Person никогда не исчезнет. –

+1

Не имеет значения, есть ли цикл сохранения. 'john' явно установлен на' nil', удаляя последнюю ссылку, тем самым заставляя ARC 'deinit' его. – Alexander

0

Вы захватываете self, что указывает на ViewController, но вам интересно узнать о примере Person.

Person на самом деле не является циркулярным, и поэтому он де-инициализирован и выпущен просто отлично, когда вы устанавливаете его на нуль в конце вашего закрытия.

Реализовать deinit для ViewController и посмотреть, как это работает.

0

Я забираю «я» в закрытии, поэтому у меня есть два сильных указателя, указывающих друг на друга, поэтому сообщение «deinit» никогда не должно быть вызвано для объекта Person.

Нет, у вас есть один сильный указатель, от закрытия до self. Цилиндрической ссылки с крыши нет в self. Таким образом, у вас есть directed acylic graph, что не представляет проблемы для ARC.

Однако ваш эксперимент испорчен, с самого начала. Даже если захват был захвачен, объект John AppleseedPerson будет по-прежнему deinit. Жизненный цикл этого объекта зависит исключительно от ссылки john от вашего ViewController. Когда вы установите эту ссылку на nil, вы удаляете последнюю ссылку на объект John Appleseed, поэтому она деинициализируется.

Смежные вопросы