gpt4 book ai didi

iphone - 使用父/子上下文时批量大小不起作用

转载 作者:技术小花猫 更新时间:2023-10-29 11:05:03 26 4
gpt4 key购买 nike

我已经能够在我的应用程序中确认这一点,并且我创建了一个快速示例应用程序来确认这一点。这是设置:

您有两个托管对象上下文:

masterMOC: NSPrivateQueueConcurrencyType, tied to persistent store coordinator
mainMOC: NSMainQueueConcurrencyType, child of masterMOC, NOT tied to any store coordinator

这个设置的灵感来自 WWDC 视频,它建议我们可以通过将 masterMOC 设置为私有(private)队列并将其绑定(bind)到持久存储来节省后台线程。如果您使用 mainMOC 设置一个 NSFetchedResultsController(它必须是 mainMOC,因为它与 UI 绑定(bind)),并设置一个fetchBatchSize,批量大小被忽略,所有实体立即出错。我启用了 SQLite 调试注释,当滚动浏览数千行(批处理大小为 20)时,没有触发任何错误。

如果我做一个简单的调整,即将持久存储协调器绑定(bind)到 mainMOC 并使其成为根上下文(也就是说,它不再是 master 的子级),那么批量大小完美运行,当我滚动浏览数千行时,触发了几个错误。

这是预期的行为吗?我错过了什么吗?

您可以下载示例项目here

最佳答案

文档中对嵌套上下文的讨论有限,它只出现在“Core Data Release Notes for iOS v5.0”和 UIManagedDocument 中。关于获取和嵌套上下文的唯一评论是:

fetch and save operations are mediated by the parent context instead of a coordinator.

鉴于缺少与使用嵌套上下文进行批量获取的功能相关的任何免责声明,我建议批量获取和嵌套上下文不兼容是不可能的。然而,这似乎是最基本的例子不起作用的情况。 (参见下面的测试代码)

这里还有一个描述相同问题的公开雷达提交:http://openradar.appspot.com/11235622 ,以及 FetchedResultsControllers 和嵌套上下文中提到的其他问题:Duplication of entity when change made by a child ManagedObjectContext is pushed (saved) to its parent .

一个可能的部分解决方案是将 NSMainQueueConcurrencyType 的附加 NSManagedObjectContext 直接添加到相同的 NSPersistentStoreCoordinator ,其唯一目的是为 NSFetchedResultsController。然后,当用户选择项目时,可以将 ObjectID 交还给嵌套子上下文,然后可以在嵌套上下文中执行任何后续编辑。

这显然会降低使用嵌套上下文的好处,并且需要更频繁地保存以在嵌套上下文和 NSFetchedResultsControllers 上下文之间进行同步。然而,根据应用程序的设计以及嵌套上下文与批量加载的相对优势,这可能会有用。 (参见下面的示例代码)

测试代码显示在嵌套上下文中最简单的批量获取失败:

#import "AppDelegate.h"

// Xcode 4.3.3:
// Create a new iOS Master-Detail project called "BatchTest" tick the "Use Core Data" check box.
// Delete all files except the AppDelegate and the BatchTest data model (leave supporting files).
// Delete all properties and methods from AppDelegate.h
// Paste this code into AppDelegate.m
// Switch on core data debugging by editing the "BatchTest" scheme and adding
// -com.apple.CoreData.SQLDebug 1
// To the "arguments passed on launch" list in the "Run" step
// Run.

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

/////////////////////////////////////////////////////////////////////////////////////
// Setup the core data stack.
/////////////////////////////////////////////////////////////////////////////////////
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"BatchTest" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

NSURL *appDocsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [appDocsDirectory URLByAppendingPathComponent:@"BatchTest.sqlite"];

NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];

NSManagedObjectContext *parentContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
parentContext.persistentStoreCoordinator = coordinator;

NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
childContext.parentContext = parentContext;


/////////////////////////////////////////////////////////////////////////////////////
// Load some test data and reset the context.
/////////////////////////////////////////////////////////////////////////////////////
[parentContext performBlockAndWait:^{
for (int i=0; i<1000; i++) {
[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:parentContext];
}
[parentContext save:nil];
[parentContext reset];
}];


/////////////////////////////////////////////////////////////////////////////////////
// Test Batched Fetching
/////////////////////////////////////////////////////////////////////////////////////

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
request.fetchBatchSize = 10;

// Fetch from the child.
NSArray *results = [childContext executeFetchRequest:request error:nil];
NSLog(@"Object 500: %@", [results objectAtIndex:500]);
// Result is all 1000 rows fetched in full, no subsequent batch fetching for event 500.

[childContext reset];
[parentContext performBlockAndWait:^{
[parentContext reset];

// Fetch from the parent.
NSArray *results = [parentContext executeFetchRequest:request error:nil];
NSLog(@"Object 500: %@", [results objectAtIndex:500]);
// Result is 1000 primary keys fetched, followed by a batch of 10 rows to find event 500.

}];

return YES;
}

@end

示例代码显示了使用附加上下文来为 NSFetchedResultsController 提供批处理工作:

#import "AppDelegate.h"

// Xcode 4.3.3:
// Create a new iOS Master-Detail project called "BatchTest" tick the "Use Core Data" check box.
// Delete all files except the AppDelegate and the BatchTest data model (leave supporting files).
// Delete all properties and methods from AppDelegate.h
// Paste this code into AppDelegate.m
// Switch on core data debugging by editing the "BatchTest" scheme and adding
// -com.apple.CoreData.SQLDebug 1
// To the "arguments passed on launch" list in the "Run" step
// Run.

@interface AppDelegate () {
NSManagedObjectContext *backgroundContext;
NSManagedObjectContext *editingContext;
NSManagedObjectContext *fetchedResultsControllerContext;
NSManagedObject *selectedObject;
}

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

/////////////////////////////////////////////////////////////////////////////////////
// Setup the core data stack.
/////////////////////////////////////////////////////////////////////////////////////
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"BatchTest" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

NSURL *appDocsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [appDocsDirectory URLByAppendingPathComponent:@"BatchTest.sqlite"];

NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];

backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.persistentStoreCoordinator = coordinator;

editingContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
editingContext.parentContext = backgroundContext;

fetchedResultsControllerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
fetchedResultsControllerContext.persistentStoreCoordinator = coordinator;

/////////////////////////////////////////////////////////////////////////////////////
// Load some test data and reset the context.
/////////////////////////////////////////////////////////////////////////////////////
[backgroundContext performBlockAndWait:^{
for (int i=0; i<1000; i++) {
[NSEntityDescription insertNewObjectForEntityForName:@"Event" inManagedObjectContext:backgroundContext];
}
[backgroundContext save:nil];
[backgroundContext reset];
}];

/////////////////////////////////////////////////////////////////////////////////////
// Example of three contexts performing different roles.
/////////////////////////////////////////////////////////////////////////////////////

// The fetchedResultsControllerContext will batch correctly as it is tied directly
// to the persistent store. It can be used to drive the UI as it is a Main Queue context.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
request.fetchBatchSize = 10;
NSArray *fetchResults = [fetchedResultsControllerContext executeFetchRequest:request error:nil];

// User selects an object in the fetchedResultsControllerContext (i.e. in a UITableView).
selectedObject = [fetchResults lastObject];
NSLog(@"**** selectedObject.timeStamp before editing:%@", [selectedObject valueForKey:@"timeStamp"]);

// Pass the object to the editing context for editing using its objectID.
NSManagedObjectID *selectedObjectID = selectedObject.objectID;
NSManagedObject *objectForEditing = [editingContext objectWithID:selectedObjectID];

// Edit the object
[objectForEditing setValue:[NSDate date] forKey:@"timeStamp"];

// Subscribe to save notifications of the background context so the
// fetchedResultsControllerContext will be updated after the background save occurs.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundContextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:backgroundContext];

// Save the editing context to push changes up to the parent, then background save.
[editingContext save:nil];
[backgroundContext performBlock:^{
[backgroundContext save:nil];
}];

return YES;
}

- (void)backgroundContextDidSave:(NSNotification *)notification {

[fetchedResultsControllerContext mergeChangesFromContextDidSaveNotification:notification];
NSLog(@"**** selectedObject.timeStamp after editing:%@", [selectedObject valueForKey:@"timeStamp"]);
// Merging changes into the fetchedResultsControllerContext would trigger updates
// to an NSFetchedResultsController and it's UITableView where these set up.

}

@end

关于iphone - 使用父/子上下文时批量大小不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11365589/

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