Memory management done right с сохранением Clean архитектуры

Nefedov Denis
Спорим с архитектором
2 min readFeb 20, 2020

Недавно осознал, что делать ссылки weak, для избежания retain cycle, внутри слоя презентации(Presenter) — не совсем правильное архитектурное решение.

  • Во-первых, memory management — это ответственность, а Presenter ей наделен не должен быть.
  • Во-вторых, таким образом мы создаем неявную зависимость в Presenter от объектов, которые кто-то должен держать по strong ссылке.
  • И, в-третьих, мы вынуждены добавить ограничение для протокола, сделав его class type.

Для решения этой проблемы я решил использовать Virtual Proxy паттерн. Он позволил делегировать ответственность за memory management главному слою, который занимается компоновкой всех объектов.

Для того, чтобы продемонстрировать решение, я создал простенький проект. Он доступен тут.
На этом коммите проект содержит retain cycle. На этом коммите в проект добавляется Virtual Proxy обертка для избежания retain cycle.

Схематично проект можно изобразить следующим образом:

Исходник: https://drive.google.com/file/d/1TxY7idlJC1ZMSn83m6a7jDaoTcj9MA18/view?usp=sharing

Как видно из схемы, у нас есть чистое разделение на слои, и ни один из слоев ниже не знает о слоях выше. Бизнес-логика четко отделана ото всех и не имеет внешних зависимостей. Слой презентации зависит от бизнес-логики, но не имеет понятия о слое UI. UI зависит от UIKit’a и слоя презентации, также он имеет неявную зависимость от бизнес-логики через замыкание. Я специально обозначил линию пунктиром, чтобы было понятно, что зависимость неявная, т.к. композицией всех объектов занимается Main модуль, в моем случае это SceneDelegate. Эта схема позволяет наглядно продемонстрировать чистую архитектуру и retain cycle в ней.

Первое и самое простое решение, это просто сделать weak зависимость в CurrencyPresenter от CurrencyPresenterOutput. В целом, это решение нормально сработает, но оно реально портит архитектуру. Как я уже написал выше:

  • Во-первых, memory management — это ответственность, а Presenter ей наделен не должен быть.
  • Во-вторых, мы вынуждены добавить ограничение для протокола, сделав его class type.
  • И, в-третьих, в дальнейшем нереально будет подсунуть в Presenter что-то, что не имеет strong внешней зависимости(logger,mock,статистику). В случае с UIViewController’ом — его держит UIKit.

Поэтому решением в данном случае является использование Virtual Proxy паттерна, а ответственность за memmory management передается main модулю, который выступает в роли composer’а.

На этом коммите в проект добавляется Virtual Proxy обертка для избежания retain cycle.

--

--