2

这是我正在尝试做的简化版本:

我有一个使用 UITabBarController 作为根控制器的应用程序。在两个选项卡中,我有一个 UINavigationController,其中包含一个自定义 UITableViewController。

其中一个 Tableview 用于显示“特色”项目,另一个用于显示“用户”项目。

第一个 API 端点是:/api/items/featured,另一个端点是:/api/items/user

我经历了在我的 AppDelegate.m 中设置所有内容的整个过程:

// Configure the object manager
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:API_SERVER]];
objectManager.managedObjectStore = managedObjectStore;
[RKObjectManager setSharedManager:objectManager];

RKEntityMapping *entityMapping = [RKEntityMapping mappingForEntityForName:@"Item" inManagedObjectStore:managedObjectStore];
[entityMapping addAttributeMappingsFromDictionary:@{
 @"id":                     @"itemID",
 @"type":                   @"type",
 @"created_at":             @"created_at":
 @"field1":                 @"field1"}];

// User Descriptor
RKResponseDescriptor *userDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:@"/api/items/user" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

[objectManager addResponseDescriptor:userDescriptor];

// Featured Listing Decriptor
RKResponseDescriptor *featuredDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:entityMapping pathPattern:@"/api/items/featured" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

[objectManager addResponseDescriptor:responseDescriptor];

然后在 TableViewControls 我有以下内容(在第二个 tableview 控制器中用 'user' 代替 'featured')

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self loadItems];
}

- (void)loadItems {    
    [[RKObjectManager sharedManager] getObjectsAtPath:@"/api/items/featured" parameters:nil 
        success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
    self.lastupdate = [[NSDate alloc] init];
    } 
        failure:^(RKObjectRequestOperation *operation, NSError *error) {
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"An Error Has Occurred" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alertView show];
    }];
}

- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController != nil) {
        return _fetchedResultsController;
    }
    RKManagedObjectStore *managedObjectStore = [RKManagedObjectStore defaultStore];
    self.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    // Edit the entity name as appropriate.
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.managedObjectContext];

    [fetchRequest setEntity:entity];

    // Set the batch size to a suitable number.
    [fetchRequest setFetchBatchSize:20];

    // Edit the sort key as appropriate.
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"created_at" ascending:NO];
    NSArray *sortDescriptors = @[sortDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    // Set predicate to only get Featured Listings
    NSPredicate *predicate = [NSPredicate predicateWithFormat: @"type like 'featured'"];
    [fetchRequest setPredicate:predicate];

    // Edit the section name key path and cache name if appropriate.
    // nil for section name key path means "no sections".
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
    aFetchedResultsController.delegate = self;
    self.fetchedResultsController = aFetchedResultsController;

    NSError *error = nil;

    if (![self.fetchedResultsController performFetch:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }

    return _fetchedResultsController;
}

现在这一切都适用于第一个选项卡(目前它返回 8 个项目,type = 'featured',显示在表中)。

当我切换到第二个选项卡时,问题就来了。它目前只返回 1 个 type='user' 的项目,并崩溃:

CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的一个错误。 * -[__NSArrayM objectAtIndex:]:索引 3 超出了带有 userInfo 的空数组的界限(null)

如果我省略了“用户”选项卡上的谓词,那么“特色”选项卡可以正常工作,“用户”选项卡会在其表中获取“特色”项目,而“用户”项目在顶部。

由于两个 tableview 控件的代码是相同的,除了“用户”和“功能”之外,我不知道问题出在哪里。

4

2 回答 2

1

您是否在 UITableViewController 上实现了 NSFetchedResultsControllerDelegate 更改方法?我在使用 RestKit/RKGist 作为模板时发现了一个类似的错误,在删除了这些更改方法并删除了控制器作为它们的委托后,这个错误就消失了。这对我有用,因为我不想以这种方式收到有关更改的通知:我将 fetchedResultsController 的 getter 从结果的重新获取中分离出来,并在相应的 RKObjectRequestOperation 的成功块中调用重新获取。希望有帮助!

编辑

在我的情况下,我实际上在一个 UITableViewController 中使用了两个 NSFetchedResultsController:

在我的 .h 界面中

@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsOneController;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsTwoController;

在 .m 实现中:

每个都有一个相应的 getter,它调用雕刻出来的 refetch

- (NSFetchedResultsController *)fetchedResultsOneController
{
    if (_fetchedResultsOneController != nil) {
        return _fetchedResultsOneController;
    }

    [self refetchResultsOneController];

    return _fetchedResultsOneController;
}

refetch 只包含构建 fetch 请求、实体描述、排序和 fetch 控制器的其余逻辑,例如:

- (void) refetchResultsOneController
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = ...;
    [fetchRequest setEntity:entity];

    // set predicate for venue name
    NSPredicate *predicate = [NSPredicate predicateWithFormat: ....];
    [fetchRequest setPredicate:predicate];

    [fetchRequest setFetchBatchSize:200];

    NSSortDescriptor *distanceDescriptor = [[NSSortDescriptor alloc] initWithKey:...];
    NSArray *sortDescriptors = @[distanceDescriptor];

    [fetchRequest setSortDescriptors:sortDescriptors];

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"SomeUniqueName"];
    self.fetchedResultsOneController = aFetchedResultsController;

    NSError *error = nil;
    if (![self.fetchedResultsOneController performFetch:&error]) {
        .. error handle...;
    }
}

请注意,refetchers 是不同的;不同的实体和不同的 nspredicates,但相同的 ManagedObjectContext。

并在 objectmanager 操作的成功块中调用 refetch,对于 resultsOne,它很简单:

     [objectManager getObjectsAtPath:[NSString stringWithFormat:@"...the path for results one...", venueId] parameters:queryParams
                        success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                            [self refetchResultsOneController];
                            [self.tableView reloadData];
                        }
                        failure:^(RKObjectRequestOperation *operation, NSError *error) {
                            NSLog(@"Error: %@", [error localizedDescription]);
                        }];

这对我来说适用于一个 UITableViewController 中的两个 NSFetchResultsController。注意我不允许用户操作来编辑/删除 fetchController 中的对象(没有双向通信) - 但我只对读取那里的数据感兴趣,并且它总是只有通过成功刷新才能启动远程服务器。

希望这可以帮助!

于 2013-05-23T16:10:42.350 回答
1

创建和配置获取的结果控制器时,不要使用缓存。目前他们都在使用缓存,@"Master"这将导致他们尝试共享不合适的信息,因为获取请求不同。

于 2013-05-20T19:15:43.327 回答