gpt4 book ai didi

ios - 添加许多对象时,NSFetchedResultsControllerDelegate 实现导致 UI 滞后

转载 作者:行者123 更新时间:2023-11-29 11:35:17 25 4
gpt4 key购买 nike

我有一个示例应用程序,它有一个带有单个属性的 NSManagedObject(我们称类为 CustomObject)。

该应用程序有一个简单的用户界面,由一个带有 UICollectionView 的 View Controller 组成,使用 NSFetchedResultsController 作为基础数据源。 NSFetchedResultsController 也设置为使用 View Controller ,因为它是 NSFetchedResultsControllerDelegate 委托(delegate)。此外,我还有一些带有按钮的工具栏,用于添加/删除 CustomObject 项。

一个按钮将导致创建 10,000 个 CustomObject 实体。我希望能够按下此按钮,一旦实体被保存到磁盘/准备好使用,UICollectionView 应该更新以反射(reflect)新值。我 100% 知道会有某种延迟 - 像这样保存值不是即时的 - 我希望延迟发生在后台线程上以防止 UI 锁定!

尽管据我所知我已正确设置,但每当我实现三个“重要”NSFetchedResultsControllerDelegate 方法中的任何一个时,我都会遇到 UI 延迟。 UI 滞后持续时间根据数据存储中已有的 CustomObject 数量而有所不同。例如,如果我只添加前 10,000 个,则只有少量延迟,不到 1 秒。如果我已经添加了 50,000 个 CustomObject,延迟会更加明显,在某些情况下会持续超过 5-10 秒。

正如我所说,仅当我实现 controllerWillChangeContentcontroller:didChangeObject:atIndexPath:forChangeType:newIndexPathcontrollerDidChangeContent< 时,UI 延迟才会出现 方法。即使方法中没有实际实现,只是因为它在 View Controller 中,所以插入速度很慢。

知道我做错了什么吗?有没有解决的办法?这是一些代码:

CoreDataController.m - 负责设置核心数据栈

//
// CoreDataController.m
// CoreDataTestApp
//
// Created by ZOlbrys on 4/12/18.
// Copyright © 2018 ZOlbrys. All rights reserved.
//

#import "CoreDataController.h"
#import <UIKit/UIKit.h>

@interface CoreDataController()

@property (nonatomic, strong) NSURL *persistentStoreURL;
@property (nonatomic, strong) NSURL *managedObjectModelURL;

@property (nonatomic, strong, readwrite) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, strong, readwrite) NSManagedObjectContext *managedObjectContext;

@end

@implementation CoreDataController

+ (CoreDataController *)sharedController {
static CoreDataController *sharedController;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
NSURL *managedObjectModelURL = [NSURL fileURLWithPath:[[NSBundle bundleForClass:[self class]] pathForResource:@"CoreDataTestApp" ofType:@"momd"]];

NSString *applicationDocumentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSURL *persistentStoreURL = [NSURL fileURLWithPath:[applicationDocumentsDirectory stringByAppendingPathComponent:@"CoreDataTestApp.sqlite"]];

sharedController = [[CoreDataController alloc] initWithManagedObjectModelURL:managedObjectModelURL persistentStoreURL:persistentStoreURL];
});

return sharedController;
}

- (instancetype)initWithManagedObjectModelURL:(NSURL*)managedObjectModelURL persistentStoreURL:(NSURL*)persistentStoreURL {
self = [super init];

if (self) {
self.persistentStoreURL = persistentStoreURL;
self.managedObjectModelURL = managedObjectModelURL;

[self setupCoreDataStack];
}

return self;
}

- (void)setupCoreDataStack {
// setup managed object model
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:self.managedObjectModelURL];

// setup persistent store coordinator
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
[_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:self.persistentStoreURL options:nil error:nil];

// setup MOC
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
}

@end

ViewController.m - 负责,嗯, View Controller 的东西!

//
// ViewController.m
// CoreDataTestApp
//
// Created by ZOlbrys on 4/12/18.
// Copyright © 2018 ZOlbrys. All rights reserved.
//

#import "ViewController.h"
#import "CustomCell.h"
#import "CoreDataController.h"
#import "CustomObject+CoreDataClass.h"

static NSString *const CUSTOM_CELL_REUSE_IDENTIFIER = @"CUSTOM_CELL_REUSE_IDENTIFIER";

@interface ViewController ()<UICollectionViewDataSource, UICollectionViewDelegate, NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.collectionView.dataSource = self;
self.collectionView.delegate = self;

[self setupFetchedResultsController];
}

- (void)setupFetchedResultsController {
NSFetchRequest<CustomObject *> *fetchRequest = [CustomObject fetchRequest];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"identifier" ascending:YES] ];

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[CoreDataController sharedController].managedObjectContext sectionNameKeyPath:nil cacheName:nil];

self.fetchedResultsController.delegate = self;

[self.fetchedResultsController performFetch:nil];

[self.collectionView reloadData];
}

- (void)addObjects:(NSUInteger)objectCount {
NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = [CoreDataController sharedController].managedObjectContext;

[temporaryContext performBlock:^{
for (int i = 0; i < objectCount; i++) {
CustomObject *object = [NSEntityDescription insertNewObjectForEntityForName:@"CustomObject" inManagedObjectContext:temporaryContext];
object.identifier = [[NSUUID UUID] UUIDString];
}

NSError *error;
if (![temporaryContext save:&error]) {
NSLog(@"Error: %@", error);
}

[[CoreDataController sharedController].managedObjectContext performBlock:^{
NSError *error;
if (![[CoreDataController sharedController].managedObjectContext save:&error]) {
NSLog(@"Error: %@", error);
}
}];
}];
}

- (void)removeObjects:(NSUInteger)objectCount {
NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = [CoreDataController sharedController].managedObjectContext;

[temporaryContext performBlock:^{
NSFetchRequest<CustomObject *> *fetchRequest = [CustomObject fetchRequest];
fetchRequest.sortDescriptors = @[ [[NSSortDescriptor alloc] initWithKey:@"identifier" ascending:YES] ];
fetchRequest.fetchLimit = objectCount;

NSFetchedResultsController<CustomObject *> *fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:temporaryContext sectionNameKeyPath:nil cacheName:nil];

[fetchedResultsController performFetch:nil];

for (CustomObject *object in fetchedResultsController.fetchedObjects) {
[temporaryContext deleteObject:object];
}

NSError *error;
if (![temporaryContext save:&error]) {
NSLog(@"Error: %@", error);
}

[[CoreDataController sharedController].managedObjectContext performBlock:^{
NSError *error;
if (![[CoreDataController sharedController].managedObjectContext save:&error]) {
NSLog(@"Error: %@", error);
}
}];
}];
}

- (IBAction)add10k:(id)sender {
[self addObjects:10000];
}

- (IBAction)add1:(id)sender {
[self addObjects:1];
}

- (IBAction)refetchData:(id)sender {
[self.fetchedResultsController performFetch:nil];

[self.collectionView reloadData];
}

- (IBAction)remove1:(id)sender {
[self removeObjects:1];
}

- (IBAction)remove10k:(id)sender {
[self removeObjects:10000];
}

#pragma mark UICollectionViewDataSource

- (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.fetchedResultsController.sections objectAtIndex:section].numberOfObjects;
}

- (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
CustomCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:CUSTOM_CELL_REUSE_IDENTIFIER forIndexPath:indexPath];

[cell setDisplayText:@"TODO"];

cell.backgroundColor = [UIColor blueColor];

return cell;
}

#pragma mark NSFetchedResultsControllerDelegate

//- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// // TODO
//}
//
//- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
// // TODO
//}
//
//- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// // TODO
//}

@end

当然,我做了一些分析 - controllerWillChangeContent 上面没有注释,我看到了这个:

enter image description here

这不是我的代码——它是苹果内部代码...

这是一个显示此问题的项目的 github 链接:

Sample Core Data project

最佳答案

滞后似乎与所选的核心数据堆栈有关:

[协调员] ← [主上下文] ← [私有(private)上下文]

在这种架构下,私有(private)上下文中的每个获取/保存操作都将通过主上下文,然后阻塞 UI。更好的选择可能是交换上下文:

[协调者] ← [私有(private)背景] ← [主要背景]

这一次,私有(private)上下文操作不会阻塞 UI。

另一种选择是将两个上下文都绑定(bind)到协调器:

[私有(private)上下文] → [协调器] ← [主上下文]

对于这两种解决方案,如果需要,您需要将更新后的对象合并回主上下文,以便从 UI“查看”更改。

关于ios - 添加许多对象时,NSFetchedResultsControllerDelegate 实现导致 UI 滞后,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49869969/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com