0

Поиск retain cycle с помощью инструментов Xcode

Обложка: Поиск retain cycle с помощью инструментов Xcode

Меня зовут Антон Малыгин, я работаю iOS разработчиком около 10 лет. Расскажу про достаточно распространенную проблему.

При разработке под iOS многие разработчики часто сталкиваются c retain cycle, когда как минимум 2 объекта удерживают строгие ссылки друг на друга. Самый простой кейс — когда разработчик просто забыл сделать weak у delegate (да, такое бывает) или вызвал self в escaping замыкании дочернего объекта.

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

Debug Memory Graph

В Xcode существует замечательный инструмент — Debug Memory Graph.

Debug Memory Graph на панели дебага

В режиме дебага необходимо нажать на соответствующую кнопку на дебаг панели.

После чего слева появится список всех объектов, который можно отфильтровать, чтобы найти необходимый.

Синяя иконка справа отфильтровывает все системные объекты и оставляет только объекты проекта

Это позволит увидеть граф объектов и ссылки между ними.

Пример графа ячейки UICollectionView

Проблемы

Debug Memory Graph, как и любой инструмент, имеет ошибки. Например, кажется, что если есть retain cycle, то на графе можно будет увидеть что-то такое:

Граф

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

Решение

Итак, я расскажу пару полезных нюансов, которые всегда помогали мне заставить этот инструмент, пусть и не работать идеально, но помочь мне найти retain cycle.

Первое — нельзя надеяться на то, что на графе показаны все ссылки!

Фактически, он отображает только часть ссылок, если их много. И здесь нужно уменьшить количество связей старым дедовским способом — закомментировать код, где, например, есть любые ссылки, слабые или сильные, на нужный нам объект. Так, возможно, придется сделать несколько итераций, и каждый раз в графе будут появляться ссылки, которые не отображались ранее. Пока не найдем тот объект, который держит сильную ссылку.

Второй момент — нужно включить Malloc Stack Logging, думаю, название говорит само за себя.

Заходим в редактирование схемы проекта:

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

Пример для ячейки

Описанный выше метод неоднократно помогал мне находить retain cycle в очень крупных проектах с большим количеством реактивщины, написанный большими командами.