Точна ли утечка в приборах XCode? - PullRequest
0 голосов
/ 18 апреля 2020

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

private func initTimePublisher() {
        timer.publish(every: tickInterval, on: runloop, in: runloopMode)
            .autoconnect()
            .map { self.durationCalculator.activeDuration(between: self.startDate!, and: $0) }
            .assign(to: \.activeDuration, on: self) // This should be a retain cycle which causes a memory like right?
            .store(in: &subs)
}

1 Ответ

0 голосов
/ 19 апреля 2020

Ваша программа может иметь цикл сохранения без утечки памяти.

Ваша программа имеет некоторые «корневые» значения: глобальные переменные, переменные в стеке и локальные переменные потока. Ваша программа может получить доступ к любому из этих значений «с нуля», не обращаясь к нему из какого-либо другого объекта.

Тогда ваша программа имеет другие объекты, доступные в качестве свойств объектов root: «дети» Корней. И у него есть объекты, достижимые из свойств детей: «внуки» корней. И так далее. Некоторые объекты достижимы несколькими путями из корней.

Таким образом, ваша программа имеет набор достижимых объектов: корни и все их потомки.

Любой объект, который ваша программа выделяла и не освобождала , и которого больше нет в наборе достижимости, утечка.

Теперь давайте посмотрим на код в вашем вопросе.

  • Утечка self? Вы не сказали, что такое self. Если на него ссылаются из глобальной переменной, или он находится в иерархии представления (или контроллера представления) или иным образом доступен, он не просочился.

  • Утечка издателя? Издатель, созданный вами с Timer.publish(...).autoconnect().map{...}.assign(...), не пропущен. Он содержит ссылки на self, но уничтожается при возврате initTimePublisher.

  • Не утечка AnyCancellable, хранящегося в subs? Если self не пропущено, то AnyCancellable достижимо, и поэтому оно также не пропущено.

Теперь вы можете быть смущены, потому что я сказал, что издатель уничтожен, когда initTimePublisher возвращается. Но вы все еще получаете сигналы от таймера, верно?

Когда вы звоните .assign(to:on:) (или .sink), вы создаете Subscription. Фактически, вы создаете график объектов Subscription, который обычно отражает график объектов Publisher, на которые вы подписаны. Вот ваш Publisher график:

Timer.Publisher <- Autoconnect <- Map

И поэтому, когда вы подписываетесь с помощью оператора .assign, вы получаете Subscription график, который выглядит следующим образом:

Timer.Publisher.Subscription <- Autoconnect.Subscription <- Map.Subscription
    <- Assign.Subscription

( Под капотом Combine обычно использует имя типа Conduit или Inner для типа Subscription, так что это то, что вы увидите в инспекторе памяти или при отладочной печати.)

AnyCancellable вы вернетесь из .assign, который храните в subs, - это обертка вокруг Assign.Subscription.

Итак, на графике подписки есть циклы сохранения. Объект Map.Subscription содержит замыкание, переданное вами в .map, которое содержит ссылку на self. И Assign.Subscription содержит ссылку на self и ссылку на Map.Subscription. И self содержит subs, который содержит AnyCancellable, обертывающий Assign.Subscription.

Но все эти объекты все еще достижимы, но не просочились, пока self достижим.

Если вы удаляете AnyCancellable из subs и позволяете ему быть уничтоженным, то он вызывает cancel на Assign.Subscription. Это говорит Assign.Subscription об отказе от ссылки на self и отмене восходящей подписки Map.Subscription. Затем Map.Subscription отбрасывает свою ссылку на закрытие и отменяет свою восходящую подписку. И так далее. Как только отмена распространяется вплоть до Timer.Publisher.Subscription, все циклы сохранения были прерваны, и все Subscription объекты были очищены. Нет утечек.

...