Чтобы ознакомиться с восстановлением состояния, я настоятельно рекомендую сеанс WWDC 2013 Что нового в восстановлении состояния а>. В то время как восстановление состояния было введено годом ранее в iOS 6, iOS 7 внесла некоторые заметные изменения.
Передача вперед
Используя подход «передать дубинку», в какой-то момент создается корень NSManagedObjectContext
и прикрепляется NSPersistentStoreCoordinator
. Контекст передается контроллеру представления, а последующие контроллеры дочернего представления, в свою очередь, передают этот корневой контекст или дочерний контекст.
Например, когда пользователь запускает приложение, корневой NSManagedObjectContext
создается и передается контроллеру корневого представления, который управляет NSFetchedResultsController
. Когда пользователь выбирает элемент в контроллере представления, создается новый контроллер подробного представления и передается экземпляр NSManagedObjectContext
.
Сохранение и восстановление состояния
Восстановление состояния меняет это способом, который важен для приложений, использующих Core Data в контроллерах представления. Если пользователь находится на контроллере подробного представления и отправляет приложение в фоновый режим, система создает архив восстановления с информацией, полезной для восстановления состояния, видимого, когда они ушли. Информация обо всей цепочке контроллеров представления записывается, и при перезапуске приложения она используется для восстановления состояния.
Когда это происходит, он не использует никаких настраиваемых инициализаторов, сегментов и т. Д. Протокол UIStateRestoring
определяет методы, используемые для кодирования и декодирования состояния, которые допускают некоторую степень настройки. Объекты, соответствующие NSCoding
, могут храниться в архивах восстановления, а в iOS 7 восстановление состояния было расширено на объекты модели и источники данных.
Восстановление состояния предназначено для хранения только той информации, которая требуется для восстановления видимого состояния приложения. Для приложения Core Data это означает хранение информации, необходимой для поиска объекта, в правильном постоянном хранилище.
На первый взгляд это кажется простым. В случае контроллера представления, управляющего NSFetchedResultsController
, это может означать сохранение дескрипторов предиката и сортировки. Для контроллера подробного представления, который отображает или редактирует один управляемый объект, представление URI управляемого объекта будет добавлено в архив восстановления состояния:
- (void) encodeRestorableStateWithCoder:(NSCoder *)coder {
NSManagedObjectID *objectID = [[self managedObject] objectID];
[coder encodeObject:[objectID URIRepresentation] forKey:kManagedObjectKeyPath];
[super encodeRestorableStateWithCoder:coder];
}
При восстановлении состояния вызывается метод UIStateRestoring -decodeRestorableStateWithCoder:
для восстановления объекта из заархивированной информации:
- Расшифруйте URI из архива восстановления.
- Получите идентификатор управляемого объекта для URI от координатора постоянного хранилища.
- Получить экземпляр управляемого объекта из контекста управляемого объекта для этого идентификатора управляемого объекта
Например:
- (void) decodeRestorableStateWithCoder:(NSCoder *)coder {
NSURL *objectURI = nil;
NSManagedObjectID *objectID = nil;
NSPersistentStoreCoordinator *coordinator = [[self managedObjectContext] persistentStoreCoordinator];
objectURI = [coder decodeObjectForKey:kManagedObjectKeyPath];
objectID = [coordinator managedObjectIDForURIRepresentation:objectURI];
[[self managedObjectContext] performBlock:^{
NSManagedObject *object = [self managedObjectContext] objectWithID:objectID];
[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setManagedObject:object];
}];
}];
}
И здесь все усложняется. В той точке жизненного цикла приложения, где вызывается -decodeRestorableStateWithCoder:
, контроллеру представления потребуется правильный NSManagedObjectContext
.
Pass the Baton vs. State Restoration: FIGHT!
При подходе «передать эстафету» был создан экземпляр контроллера представления в результате взаимодействия с пользователем, и в него был передан контекст управляемого объекта. Этот контекст управляемого объекта был связан с родительским контекстом или постоянным координатором хранилища.
Во время восстановления состояния этого не происходит. Если вы посмотрите на иллюстрации того, что происходит во время передачи эстафеты против восстановления состояния, они могут выглядеть очень похожими - и так оно и есть. Во время восстановления состояния передаются данные - экземпляр NSCoder
, представляющий интерфейс к архиву восстановления.
К сожалению, необходимая нам NSManagedObjectContext
информация не может быть сохранена как часть архива восстановления. NSManagedObjectContext
соответствует NSCoding
, но важные части не соответствуют. NSPersistentStoreCoordinator
нет, поэтому он не будет сохраняться. Любопытно, что свойство parentContext
у NSManagedObjectContext
также не будет (я настоятельно рекомендую зарегистрировать радар по этому поводу). Сохранение URL-адресов конкретных NSPersistentStore
экземпляров и воссоздание NSPersistentStoreCoordinator
в каждом контроллере представления может показаться привлекательным вариантом, но результатом будет отдельный координатор для каждого контроллера представления, что может быстро привести к катастрофе.
Таким образом, хотя восстановление состояния может предоставить информацию, необходимую для поиска сущностей в NSManagedObjectContext
, оно не может напрямую предоставить то, что необходимо для воссоздания самого контекста.
Так, что дальше?
В конечном итоге в -decodeRestorableStateWithCoder:
контроллера представления требуется экземпляр NSManagedObjectContext
, имеющий то же происхождение, что и при кодировании состояния. Он должен иметь одинаковую структуру контекстов предков и постоянных хранилищ.
Восстановление состояния начинается в UIApplicationDelegate, где в процессе восстановления вызываются несколько методов делегата (-application:willFinishLaunchingWithOptions:
, -application:shouldRestoreApplicationState:
, -didDecodeRestorableStateWithCoder:
, -application:viewControllerWithRestorationIdentifierPath:coder:
). Каждый из них дает возможность настроить процесс восстановления с самого начала и передать информацию, например, прикрепить экземпляр NSManagedObjectContext
в качестве ссылки на связанный объект к NSCoder
, используемому для восстановления.
Если объект делегата приложения отвечает за создание корневого контекста, этот объект может быть передан по всей цепочке контроллеров представления после завершения процесса запуска (с восстановлением состояния или без него). Каждый контроллер представления передает соответствующий экземпляр NSManagedObjectContext
своим дочерним контроллерам представления:
@implementation UIViewController (CoreData)
- (void) setManagedObjectContext:(NSManagedObjectContext *)context {
[[self childViewControllers] makeObjectsPerformSelector:_cmd withObject:context];
}
@end
И каждый контроллер представления, который предоставил свою собственную реализацию, будет создавать собственный дочерний контекст. Это имеет и другие преимущества - любой подход, при котором пользователи контекста управляемого объекта реагируют на его изменение, упрощает асинхронное создание контекста. Само создание контекста происходит быстро и легко, но добавление постоянных хранилищ в корневой контекст потенциально очень дорого и не должно выполняться в основной очереди. Многие приложения делают это в основной очереди в методе делегата приложения и в конечном итоге убиваются ОС, когда открытие файлов в хранилище занимает слишком много времени или требуется миграция. Добавление постоянного хранилища в другой поток и последующая отправка контекста объектам, которые его используют, когда он будет готов, может помочь предотвратить подобные проблемы.
Другой подход может заключаться в использовании цепочки респондентов в контроллере представления. Во время восстановления состояния контроллер представления может пройти по цепочке респондента, чтобы найти следующий NSManagedObjectContext
вверх по цепочке, создать дочерний контекст и использовать его. Реализовать это с помощью неформального протокола просто, и в результате получается гибкое и адаптируемое решение.
Реализация по умолчанию неформального протокола будет идти дальше по цепочке респондентов:
@implementation UIResponder (CoreData)
- (NSManagedObjectContext *) managedObjectContext {
NSManagedObjectContext *result = nil;
if ([self nextResponder] != nil){
if ([[self nextResponder] respondsToSelector:@selector(managedObjectContext)]){
result = [[self nextResponder] managedObjectContext];
}
}
return result;
}
@end
И любой объект в цепочке респондента может реализовать -managedObjectContext
, чтобы обеспечить альтернативную реализацию. Сюда входит делегат приложения, который действительно участвует в цепочке респондента. Используя неформальный протокол, описанный выше, если представление или контроллер представления вызывает -managedObjectContext
, реализация по умолчанию перейдет к делегату приложения, чтобы вернуть результат, если какой-либо другой объект по пути не предоставил результат, отличный от нуля.
У вас также есть возможность использовать фабрики классов восстановления с восстановлением состояния для восстановления цепочки контекстов управляемых объектов во время восстановления.
Эти решения подходят не для каждого приложения или ситуации, только вы можете решить, что подойдет вам.
person
quellish
schedule
07.05.2015