NSFetchedResultsController多个实体为UITableView [英] NSFetchedResultsController multiple entities for UITableView
问题描述
我有两个实体,一个称为Post,一个称为User。 Post<< ----> User是核心数据中的关系。我使用NSFetchedResultsController来获取我的核心数据栈中的所有Post记录,然后在UITableView中显示它们。每个单元格都有一个图像,该图像对应一个User.profilePicture。
初始化时,我不从服务器下载配置文件图片,我只下载滚动过该单元格(延迟加载)。一旦我下载它,我将下载的图像保存到核心数据堆栈中的相应User.profilePicture。
当我更新用户时,是否有一个方法调用controllerDidChangeContent实体??我目前的理解是,我的NSFetchedResultsController只能跟随Post实体,因为这是我最初设置它做,不能遍历和监视关系之间的更新,是真的吗?
在您的用户
.m文件实现 setProfilePicture:
如下:
//在多个ENV中未测试
- (void)setProfilePicture:(NSData *)data
{
[self willChangeValueForKey:@profilePicture ];
[self setPrimitiveValue:data forKey:@profilePicture];
[self.posts enumerateObjectsUsingBlock:^(Post * p,BOOL * stop){
[p willChangeValueForKey:@user];
[p didChangeValueForKey:@user];
}];
[self didChangeValueForKey:@profilePicture];
}
这将通知FRC Post元素已更改。
您可能会找到更多信息此处
编辑:
要获取访问数据,您可以将其添加到您的用户
.m:
// UNTESTED
+(void)mergeToMain:(NSNotification *)notification
{
AppDelegate * appDel =(AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDel.managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification :)
withObject:notification
waitUntilDone:YES];
}
- (NSData *)_ profilePicture
{
return [self primitiveValueForKey:@profilePicture];
}
- (NSData *)profilePicture
{
[self willAccessValueForKey:@profilePicture];
NSData * picData = [self primitiveValueForKey:@profilePicture];
if(!name){
__block NSManagedObjectID * objectID = self.objectID;
//这通过使用单个队列
//来解决所有配置文件图片下载的每个项目的多个下载。
//有更多的并发方法来完成
dispatch_async(downloadSerialQueue,^ {//定义一些串行队列,以确保你多次下载同一个对象
NSError * error = nil;
AppDelegate * appDel =(AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:appDel.persistentStoreCoordinator];
[context setUndoManager:nil];
User * user =(User *)[context existingObjectWithID:objectID error:& error];
if(user& [user _profilePicture] = nil){
NSData * data = // [从服务器检索数据的方法];
if(data){
if(user){
user.profilePicture = data ;
} else {
NSLog(@ERROR :: error fetching user:%@,error);
return;
}
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(mergeToMain :) name:NSManagedObjectContextDidSaveNotification object:context];
[context save:& error];
[[NSNotificationCenter defaultCenter] removeObserver:[self class] name:NSManagedObjectContextDidSaveNotification object:context];
}
}
});
}
[self didAccessValueForKey:@profilePicture];
return picData;
}
I have two entities one called Post and one called User. Post<<---->User is the relationship in core data. I am using a NSFetchedResultsController to fetch all Post records in my core data stack and then displaying them in a UITableView. Each cell has an image and that image corresponds to a User.profilePicture.
Upon initializing I do not download the profile picture from the server, I only download when it scrolls past that cell (lazy load). Once I download it I save the downloaded image to the corresponding User.profilePicture in the core data stack.
Is there a way for controllerDidChangeContent to be called when I update the User entity?? My current understanding is that my NSFetchedResultsController can only follow the Post entity since that is what I initially set it to do and cannot traverse and monitor updates across a relationship, is that true?
Sadly I know only of an UGLY solution for this issue.
In your User
.m file implements the setProfilePicture:
like this:
//NOT TESTED IN A MULTITHREADED ENV
- (void) setProfilePicture:(NSData *)data
{
[self willChangeValueForKey:@"profilePicture"];
[self setPrimitiveValue:data forKey:@"profilePicture"];
[self.posts enumerateObjectsUsingBlock:^(Post* p, BOOL *stop) {
[p willChangeValueForKey:@"user"];
[p didChangeValueForKey:@"user"];
}];
[self didChangeValueForKey:@"profilePicture"];
}
This will notify the FRC that the Post element has changes.
You might find additional information here
Edit:
To fetch the data on access you can add this to your User
.m:
//UNTESTED
+ (void) mergeToMain:(NSNotification*)notification
{
AppDelegate* appDel = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[appDel.managedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
- (NSData*)_profilePicture
{
return [self primitiveValueForKey:@"profilePicture"];
}
- (NSData*) profilePicture
{
[self willAccessValueForKey:@"profilePicture"];
NSData* picData = [self primitiveValueForKey:@"profilePicture"];
if (!name) {
__block NSManagedObjectID* objectID = self.objectID;
//This solves the multiple downloads per item by using a single queue
//for all profile pictures download.
//There are more concurrent ways to accomplish that
dispatch_async(downloadSerialQueue, ^{ //define some serial queue for assuring you down download multiple times the same object
NSError* error = nil;
AppDelegate* appDel = (AppDelegate*)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext* context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:appDel.persistentStoreCoordinator];
[context setUndoManager:nil];
User* user = (User*)[context existingObjectWithID:objectID error:&error];
if (user && [user _profilePicture] == nil) {
NSData *data = //[method to retrieve data from server];
if (data) {
if (user) {
user.profilePicture = data;
} else {
NSLog(@"ERROR:: error fetching user: %@",error);
return;
}
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(mergeToMain:) name:NSManagedObjectContextDidSaveNotification object:context];
[context save:&error];
[[NSNotificationCenter defaultCenter] removeObserver:[self class] name:NSManagedObjectContextDidSaveNotification object:context];
}
}
});
}
[self didAccessValueForKey:@"profilePicture"];
return picData;
}
这篇关于NSFetchedResultsController多个实体为UITableView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!