Как стать автором
Обновить

Комментарии 27

Спасибо, интересная статья.
Как раз начал использовать CoreData.
Не стоит обращаться к массиву fetchedObjects или по протоколу NSFetchedResultsSectionInfo к массиву объектов секции:


А для получения кол-ва объектов можно ведь использовать протокол NSFetchedResultsSectionInfo?
Пример для вывода кол-ва элементов для collectionView:
return [[([_fetchResultsController sections] count)] numberOfObjects];
Этот метод можете смело использовать, он не вызывает полного рефетча данных.
Спасибо! Много интересного даже для тех, кто давно использует Core Data :)
Удивляет меня скорость записи. Загрузить и распарсить json получается заметно быстрее, чем записать результат в Core Data. Чем это обусловлено?
Испокон веков завелось, что read быстрее write. Часть времени съедается на построение индекса(как минимум primary index), часть на I/O с постоянной памятью. Обычные приложения, направленные на потребление контента, большую часть времени занимаются read и write можно пренебречь. Если же ваше приложение критично к записи, то вам противопоказано разделение сущностей.
Понимаю, что запись чисто физически должна работать медленней, однако разброс 3 секунды и доля секунды всё-же не укладывается в голову.
10к записей! Вы реально телефоном 10к записей планируете инсертить?
Что фантастичного в 10к записей? Простой кейс — облачное хранилище фотографий, коих у юзера вполне может быть и больше, и соответсвенно initial sync на свежей установке.
Фантастичны не 10к записей, фантастичен такой initial sync: зачем тащить сразу все, если можно дать пользователю что-то делать с первичкой, а тащить все фоном и то если такое действительно необходимо. Да и шансы сбоя на 10к записях такие большие, что лучше дробить по 1к хотя бы, чтобы в случае любого сбоя не начинать сначала.
В реальности все так и происходит по блокам, тестирование лишь определяет average time per insert, который для нас критичен.
Не могу понять почему так популярны такие сложные структуры с контекстами в CoreData для работы в разных потоках. К примеру приведенная в этой статье или еще круче: stackoverflow.com/questions/21476559/coredata-child-contexts-nsfetchedresultscontroller-and-main-thread. Я считаю что обращаться к CoreData можно только в фоновом потоке. Простая модель позволяет минимизировать что делается под капотом, чтобы было легче контролировать объемы используемой памяти и cpu. Да, NSFetchResultController я не использую.

Работа с базой у меня идет через отдельный объект. Прочтенные Entity из базы котвертируются в NSObject производные объекты c теми-же полями и передаются в главный поток уже с помощью делегата или нотификации. Там они уже отображаются в кастомном UI (типа collectionView).

Буду рад комментариям по поводу моего подхода работы с CoreData.
Лишнюю сложность в проектировение стеков я отметил и в своей статье. Действительно, нужно исходить из требований. Если у вас приложение, где вы одновременно работает с 1-2 записями, а максимальное их количество не превышает скажем тысячи, то вам нет особого смысла вообще усложнять себе жизнь работой с бэкграундом, Core Data достаточно быстра, чтобы совладать с такими масштабами прямо на главном контексте.

Ваш подход мне кажется своебразным, но интересным. Мне хотелось бы узнать подробности как вы обрабатываете fault для ваших NSObject объектов и пагинируете большой объем данных? А также интересно как вы реагируете на изменения в контексте? Возможно это материал для написания статьи? Я бы с удовольствие почитал о вашем опыте. ;)
В моем случае объектов может быть сколько угодно и размер базы зависит от пользователя. По сути это список который мне нужно отображать в одном контроллере, обрабатывать добавление (по мере загрузки данных), удаление и пейджинацию. Manager работающий с базой хранит все методы чтобы сохранить в нее NSObject объекты и взять их. Внутри эта работа проецируется на CoreData запросы которые выполняются на одной queue через dispatch_async поэтому fault-ов не происходит. Контекст в итоге всего один, с ним я в потоке и работаю. Пейджинация происходит как вызов метода у manager-а c указанием необходимого диапазона записей, данные приходят в переданный completionBlock-e. Данные сортируются по дате. Если где-то добавляется объект в manager то он нотифицирует об этом передавая обернутый объект после записи в базу. Про статью не уверен, вроде все тут уместилось).

Меня еще терзают сомнения, стоит ли упрощенный стек этих ухищрений с модельно, но тем не менее мое мнение выражается в том, что любой подход имеет право на жизнь, если он работает и его работа всех устраивает :) Попробуйте, кстати посмотреть на методы performBlock: и performBlockAndWait: контекста, с ними не нужно реализовывать свою очередь.
Если вы не используете NSFetchResultController, и везде у вас свои модели, то зачем вам вообще Core Data? Почему бы не общаться напрямую с SQLite используя тот же FMDB? И лишнее звено в цепочке ущйдёт, и возможности SQLite можно будет использовать по полной программе.
Ну все-же плюсы CoreData очень полезны и для меня: автоматическая миграция, визуальное создание таблиц, простое оперирование с объектам и их связями между собой (автоматическая подгрузка при обращении к ним). С CoreData я думаю все-же проще работать. Я вообще не рассматривал чистый SQLite подход, возможно стоит попробовать), сразу сказать сложно.
прим. в iOS 8 появилось API, позволяющее изменять/удалять объекты прямо в хранилище;

API по изменению видел, но что нового с удалением? Разве появился способ удалить большое множество объектов, не загружая при этом их все в память, и не высасывая неявно все каскадные зависимости, которые тоже надо удалить?
Оу, большое спасибо за замечание. До нового API еще толком не дошли руки, а WWDC видео было просмотрено уже довольно давно, что-то в памяти значит каратнуло. Действительно, новое API пополнилось NSBatchUpdateRequest, название которого говорит само за себя.
Когда прочитал «Для этого нам потребуется Core Data… и все.» подумал, что вы и сами фотографии будете хранить в Core Data…
p.s. Был печальный опыт переписывания кода, который кешировал все http запросы к картинкам в core data, а потом спрашивали, почему так лагает прокрутка галереи со 100+ миниатюрами.
Понимаю вашу боль. Хотя сама Apple в своих сессиях говорит о том, что миниатюры, хранимые в Core Data — это OK, меня терзают на этот счет смутные сомнениями… тем более, что сами они хранят миниатюры в EXIF'ах фоток.
Core Data прекрасная вещь, которая при должном понимании деталей и методов работы через разные потоки превращается в крайне мощный инструмент.

Другое дело это iCloud Core Data, которая даже в iOS 7 была еще настолько ненадежна, что могла по итогу запороть любой проект.
Статья интересная, спасибо!
Реальных use cases для Core Data по пальцам пересчитать. А в свете последних тенденций ухода в сторону immutable сущностей, она становится все сложнее и сложнее для понимания.
Core Data это хорошо, но слишком много «магии» под капотом. К тому же после N выпущенных версий приложения, миграции схемы занимают просто дикое количество времени.
Лично я открыл для себя Realm, и плотно экспериментирую с immutable сущностями (примерно как это делают в Facebook).
Хотел уяснить для себя такой момент. В документации Apple и в разных примерах инициализация стека Core Data суюют в Appdelegate.
Создал для себя отдельный объект синглтон, свойсвтами которого есть managedObjectContext, FetchedResultsController и тд. Тоесть приложение все дейсвтия с кор датой делает через этот объект.
Вопрос такое — если моя модель использует 2 Entity — как сделать разграничение и создавать 2 fetchedresultscontrollerа в этом объекте? как разделять обращение к тому или другому?
заранее спасибо
NSFetchedResultsController — это не часть Core Data стэка, не нужно его хранить в нем. Если ваши данные представлены таблицей, то вам нужно использовать этот контроллер как обычный табличный контроллер, если в другом виде, то в ваше контроллере экрана просто создаете новый NSFetchedResultsController на контексте в главном потоке например в viewDidLoad и используете его данные для показа. Разграничивать 2 таких контроллера не надо, все будет сделано за вас внутри.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий