В очередной раз перебирая crash логи большого проекта мы устали выгребать тысячи падей у таблиц и collectionView из-за потоков, неверных просчетов секций, индексов и давно всеми известной темы – проблемы с апдейтами у UICollectionView. Пул реквесты принятные в нашей компании не решали эту проблему на 100%.

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

Идея

Изначальная идея выглядела примерно так:
1. Работа с таблицей и инкапсуляция ее dataSource и delegate. Мне хотелось чтобы эти вещи были в одном месте и в одном месте при необходимости я могла перегрузить нужный метод. Так же он должен включать себя регистрацию ячеек у таблицы.
2. View слой который и будет содержать таблицу или коллекшен вью.
3. Работа с ячейками, каждую из них необходимо было создать, настроить и запустить на экран.

Дальше возникла задача создать едниный класс для работы с данными, такой себе in-memory storage который будет строго типизирован и будет содержать в себе все данные для показа в таблице. В данных момент постоянно приходилось выкручиваться массивами, данные постоянно мешались в одну кучу и массив массивов для мультисекций уже порядком надоел, каждый раз приходилось делать кучи копипасты для проверок и индексов, это увеличивало шанс ошибки и коллличество кода. Так же это не решало проблемы хедеров/футеров секций и прочие вещи.
Итого: Каждая ячейка должна быть обновлена со своими данными, эти данные лучше всего собрать в модель. Значит каждой ячейке одно типа должна соответвовать модель своего типа. Для кого-то это покажется оверхедом, но согласитесь лучше строго типизировать ячейки и данные соответвующие им, чем потом пытаться найти ошибку, почему какая-либо из ячеек получает не свои данные и отображается неправильно, или еще хуже приложение падает.

 

Процесс:

Погуглив еще я наткнулась на проект DTTableViewManager и DTModelStorage на фоне всего остального они выглядели почти как то, что мне нужно, детально изучив код, я поняла что разработчик не захотел разделять слой вью и контроллера таблицы, к сожалению для меня это было не приемлимо, потому что я считаю что все вью контроллеры должны наследоваться от одного базового, потом от него может появится базовый table контроллер и collection, если нужно. Разработчик не захотел разделять эти слои, поэтому был свой приватный форк и на скорую руку разделены View слои и получился ANTableController, просто класс отвечающий за все желания и делегаты UITableView со своими твиками и апдейтами.

Продолжая работу с этим компонентом у меня появлялись новые идеи и в какой-то момент этих правок стало больше чем оригинального кода, так появился ANTableController и ANStorage.

Было добавлено:
– batchUpdate для UITableView
– дополнительные диспатчи на main thread
– проверки на валидность данных
– блок предиката для поиска
и многое другое.

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

Но так же он имел значительные недостатки, которые нужно было срочно исправлять:
– обновление данных не на мейн потоке приводили к падениям и глюкам
– очень часто наши клиенты хотели 60FPS scrolling поэтому updateWithModel: при cellForRowAtIndexPath: создавало проблемы
– если запустить пачку апдейтов, какой-то из апдейтов может закончится раньше, чем вызовет ярый дисбаланс, а отправлять их все синхронно на мейн иногда нет вохможности. Ну или код перезагрузки таблицы, к примеру pullToRefresh вызовется в то время, пока апдейт еще не закончился опять будет инконсистентность данных и падение.

Так что родилась идея встроить NSOperationQueue внутрь ANStorage и пускай он создает операции изменения, а контроллер заботливо складывает это все в очередь. А в случае релоада storage все операции отменяются и добавляется новая – по перезагрузке, что гарантирует что в одну еденицу времени будет идти только одно обновление и избавит от падений и глюков.

 

 

 

Как устроен ANStorage
Если просто – это массив массивов. Если рассмотреть его детальнее это класс который в себе содержит массив секций. и интерфейс для работы с данными очень похожий на то, что требует от нас таблица и коллекшен вью. Секция – ANStorageSectionModel – в свою очередь содержит в себе массив элементов данной секции

Что мы хотим от хранилища? Обновить с батчем – анимировано или нет, и перезагрузить – так же с анимацией или нет. Ну и сделать поиск по элементам, для облегчения работы с этим я просто сделала блок с предикатой. Как происходит генерация этих операций обновления данных? ANStorage содержит в себе
приватную проперти типа ANStorageController, он является связующим звеном между новыми данными и фабрикой по созданию операций. Давайте рассмотрим как пример метод:

– (void)addItem:(id)item toSection:(NSUInteger)sectionIndex
{
ANStorageUpdateModel* update =

[code language=”plain”][/code]

Plain Text>

[code language=”plain”][/code]

Он просто стучится в фабрику ANStorageUpdater и тот начинает свою магию

[code language=”objectivec”][/code]

[code language=”objectivec”][/code]

 

 

Итак, мы передаем какие ячейки в какую секцию мы хотим добавить, да-да именно так, наши данные будут напрямую отражены ячейками в интерфейсе.

Создаем нашу модель обновления, если мы решим на самом деле сделать пустой апдейт, и модель в конце останетс пустая, не переживайте, таблица даже не будет знать об этом, этот апдейт не будет исполнен и будет отсечен еще при добавлении в очередь. Так как этот компонент позволяет добавлять в даже еще не созданную секцию, то мы перед добавлением должны обязательно проверить существует ли она и если нет – создать, и это создание тоже добавлить в наш апдейт таблицы, иначе таблица ничего не будет знать о новой секции и все благополучно упадет на index out of bounds. Итак,
Дальше наступает момент номер два если посмотреть на коллекшен и на тейбл то плюс минус они как братья, и содержат половину одинаковых методов, так почему бы не объеденить их в один? Ну совсем в один не получится, но сложить большиснтво методов в один клас получилось – ANListController

ANStorageLog – если нужно видеть дебаг логи по сторейджу


Oksana Kovalchuk

Passionate IOS Developer

Related Posts

ios

UITextView going crazy…

  Today I stuck with some interesting UITextField behaviour. So when you call: [crayon-59ec3650181fa685129082/] And your current in this moment not visible, for example you don’t use scrolling in textView and text hidden by parent Read more...

ios

Automation in development

You can easily automate creating enum with property names, like mogenerator by using this service – http://s.oks.im/modelObjcEnumGenerator.workflow.zip Just select your property list, right click and select – Services -> modelObjcEnumGenerator. And than paste from clipboard. Thats Read more...

ios

Asset catalog system file names

Sometimes IOS become not so obvious and not clear. The same happens to me, when I try to load default splash screen. [crayon-59ec3650182c6652013882/] Example for icons: [crayon-59ec3650182ca212382286/] Related