后台位置的核心数据更新位置导致阻塞 UI
Core Data update locations for positions in background cause blocking UI
我正在使用 3 Managed Object Contexts Architecture(为父级为 managedObjectContext 的后台创建临时上下文 - UI,并且其父级 writerObjectContext 应在后台写入数据库)并且我遇到阻塞 UI 当我更新对象时。例子最好。所以我的数据库中有数千个点,我使用 NSFetchedResultsController
和 tableView
来获取它们。这是我的代码:
- (void)viewDidLoad
{
[super viewDidLoad];
temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = [[CoreDataManager manager] managedObjectContext];
temporaryContext.undoManager = nil;
...
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PositionCellIdentifier forIndexPath:indexPath];
[self configureCell:(PanelPositionCell *)cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(PanelPositionCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Fetch Record
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
OpenPositionCD *position = (OpenPositionCD *)record;
// Update Cell
[cell setValuesByOpenPositionCD:position];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self checkAddress:position];
});
}
- (void)checkAddress:(OpenPositionCD *)openPosition {
if (openPosition.latitude == 0 && openPosition.longitude == 0) {
return;
}
if ([openPosition hasAddress]) {
return;
}
CLLocation *location = [[CLLocation alloc]initWithLatitude:[openPosition.latitude doubleValue] longitude:[openPosition.longitude doubleValue]];
[[LocationManager manager] getPlacemarksForLocation:location withCompletion:^(NSArray *placemarks, NSError *error) {
if (!error) {
openPosition.address = placemarks[0];
NSError *error = nil;
if (![temporaryContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
}
}];
}
当我滚动到没有地址的单元格时,UI 经常冻结,这取决于我滚动的速度。那么我该如何解决呢?我正在尝试 with/without dispatch_async
和 with/without temporaryContext performBlock
但看起来没有任何帮助。所以感谢您的帮助。
我正在 CoreDataManager 中添加上下文初始化,但我希望没问题:
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = [self writerManagedObjectContext];
return _managedObjectContext;
}
// Writer context for database
- (NSManagedObjectContext *)writerManagedObjectContext{
if (_writerManagedObjectContext != nil) {
return _writerManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_writerManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _writerManagedObjectContext;
}
您正在使用过时的 API。使用多个上下文的推荐方法是 而不是 将相同的持久存储协调器分配给子上下文,而是将其分配给 parentContext
。
您可能想要 M. Zarra 的设置
WriterContext (background)
MainContext (main thread, parent is WriterContext)
WorkerContext (background, parent is MainContext, create and destroy as needed)
您将在 worker 上下文中执行后台工作,save
将更改推送到主上下文。您可以在方便的时候保存主上下文,数据存储仅在编写器上下文保存时在后台被访问。
最后,您在另一个线程中使用了位置对象。您需要将调用包装到工作上下文的 performBlock
块中以安全地使用这些对象。
我正在使用 3 Managed Object Contexts Architecture(为父级为 managedObjectContext 的后台创建临时上下文 - UI,并且其父级 writerObjectContext 应在后台写入数据库)并且我遇到阻塞 UI 当我更新对象时。例子最好。所以我的数据库中有数千个点,我使用 NSFetchedResultsController
和 tableView
来获取它们。这是我的代码:
- (void)viewDidLoad
{
[super viewDidLoad];
temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = [[CoreDataManager manager] managedObjectContext];
temporaryContext.undoManager = nil;
...
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PositionCellIdentifier forIndexPath:indexPath];
[self configureCell:(PanelPositionCell *)cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(PanelPositionCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Fetch Record
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
OpenPositionCD *position = (OpenPositionCD *)record;
// Update Cell
[cell setValuesByOpenPositionCD:position];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self checkAddress:position];
});
}
- (void)checkAddress:(OpenPositionCD *)openPosition {
if (openPosition.latitude == 0 && openPosition.longitude == 0) {
return;
}
if ([openPosition hasAddress]) {
return;
}
CLLocation *location = [[CLLocation alloc]initWithLatitude:[openPosition.latitude doubleValue] longitude:[openPosition.longitude doubleValue]];
[[LocationManager manager] getPlacemarksForLocation:location withCompletion:^(NSArray *placemarks, NSError *error) {
if (!error) {
openPosition.address = placemarks[0];
NSError *error = nil;
if (![temporaryContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
}
}];
}
当我滚动到没有地址的单元格时,UI 经常冻结,这取决于我滚动的速度。那么我该如何解决呢?我正在尝试 with/without dispatch_async
和 with/without temporaryContext performBlock
但看起来没有任何帮助。所以感谢您的帮助。
我正在 CoreDataManager 中添加上下文初始化,但我希望没问题:
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext{
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
_managedObjectContext.parentContext = [self writerManagedObjectContext];
return _managedObjectContext;
}
// Writer context for database
- (NSManagedObjectContext *)writerManagedObjectContext{
if (_writerManagedObjectContext != nil) {
return _writerManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
_writerManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_writerManagedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _writerManagedObjectContext;
}
您正在使用过时的 API。使用多个上下文的推荐方法是 而不是 将相同的持久存储协调器分配给子上下文,而是将其分配给 parentContext
。
您可能想要 M. Zarra 的设置
WriterContext (background)
MainContext (main thread, parent is WriterContext)
WorkerContext (background, parent is MainContext, create and destroy as needed)
您将在 worker 上下文中执行后台工作,save
将更改推送到主上下文。您可以在方便的时候保存主上下文,数据存储仅在编写器上下文保存时在后台被访问。
最后,您在另一个线程中使用了位置对象。您需要将调用包装到工作上下文的 performBlock
块中以安全地使用这些对象。