【问题标题】:NSFetchedResultsController multiple entities for UITableViewUITableView 的 NSFetchedResultsController 多个实体
【发布时间】:2013-04-26 17:46:29
【问题描述】:

我有两个实体,一个称为 Post,一个称为 User。 PostUser 是核心数据中的关系。我正在使用 NSFetchedResultsController 来获取我的核心数据堆栈中的所有 Post 记录,然后将它们显示在 UITableView 中。每个单元格都有一个图像,该图像对应于一个 User.profilePicture。

初始化时,我不从服务器下载个人资料图片,我只在它滚动经过该单元格时下载(延迟加载)。下载后,我将下载的图像保存到核心数据堆栈中相应的 User.profilePicture 中。

当我更新用户实体时,有没有办法调用 controllerDidChangeContent?我目前的理解是,我的 NSFetchedResultsController 只能跟随 Post 实体,因为这是我最初设置的,不能遍历和监控关系中的更新,是这样吗?

【问题讨论】:

    标签: ios core-data nsfetchedresultscontroller nsfetchrequest nsentitydescription


    【解决方案1】:

    遗憾的是,我只知道针对此问题的 UGLY 解决方案。

    在您的User .m 文件中实现setProfilePicture:,如下所示:

    //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"];
    }
    

    这将通知 FRC Post 元素发生了变化。

    您可能会发现更多信息here

    编辑:

    要获取访问数据,您可以将其添加到您的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;
    }
    

    【讨论】:

    • hmm 我得试试这个,它看起来并不难看……在多线程环境中,这些 KVO 消息需要在主线程上吗?
    • 我相信即使到那时它也可以工作,但还没有测试过(稍后会这样做)
    • 已测试,在合并其他线程的更改时会更新。
    • 我将在第二天访问 Xcode 并更新状态时测试我的实现。谢谢你的帮助。一个问题,我正计划实施这个要点...gist.github.com/jkemink/45458176c7a9a5f7213d ....基本上我调用该属性,如果尚未下载,我返回 nil/blank 值,下载它,设置它,kvo 启动并然后更新我的表格视图。我的要点在某种程度上是否有意义,或者这样做很丑陋。
    • 这是非常危险的。如果您在下载过程中取消分配上下文并且没有正确保存,您将丢失数据。我建议使用objectID 执行获取并导入并设置成功的数据,并将当前获取的objectID 维护在中心位置以避免不必要/重复的下载。
    【解决方案2】:

    我认为这个问题可以在不涉及 NSFetchedResultsController 的情况下解决。

    1. 使用SDWebImage,SDWebImage可以从远程服务器异步加载图片,这样做:

      [myImageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                     placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
      
    2. 使用 KVO,将观察者添加到用户实体并相应地更新相应的图像视图。但是KVO的代码比较复杂,ReactiveCocoa可以简化一下:

      [RACAble(user.profilePicture) subscribeNext:^(UIImage *image) {
          [myImageView setImage:image];
      }];
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多