FetchedResultsController returns null 即使核心数据实体有数据

FetchedResultsController returns null even when core data entity has data

我已将 table 与自定义 UITableViewCell 分组。我正在使用 AFNetworking 调用 Web 服务并将它们存储在核心数据实体中。我使用 NSFetchedResultsViewController 显示来自 Core Data table 的数据。我还在 table 视图上进行了搜索。由于某种原因,NSFetchedResultsController returns 没有数据,即使核心数据实体中有数据。你能帮忙解决这个问题吗?这是我的代码。

#import "MasterViewController.h"
#import "DetailViewController.h"

@interface MasterViewController ()
@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;

@end

@implementation MasterViewController
@synthesize fetchedResultsController;

- (void)viewDidLoad {
    [super viewDidLoad];
    appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
}

-(void)viewDidAppear:(BOOL)animated
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"RackStatusViewLoaded" object:self];
    self.navigationItem.title = [defaults objectForKey:@"loggedInUserSelectedStoreName"];
    NSString *activeStockTakeRequestPath = [NSString stringWithFormat:@"%@/stocktake/store/%@/activestocktake",[appDelegate baseUrl],[defaults objectForKey:@"loggedInUserSelectedStoreId"]];
    
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[appDelegate baseUrl]]];
    NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
                                                            path:activeStockTakeRequestPath
                                                      parameters:nil];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSInteger statusCode = operation.response.statusCode;
       // NSLog(@"AFNetowrking Response: %@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
        NSLog(@"AF Status Code %d",statusCode);
        NSError *error;
        NSMutableDictionary *returnedDict = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
        if (statusCode==200)
        {
            NSLog(@"Status 200");
            [defaults setBool:true forKey:@"isActiveStockTakeForCurrentStore"];
            [defaults setObject:returnedDict[@"id"] forKey:@"ActiveStockTakeid"];
            [defaults setObject:returnedDict[@"name"] forKey:@"ActiveStockTakeName"];
            [defaults setObject:returnedDict[@"onDate"] forKey:@"ActiveStockTakeDate"];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"setTitleForCurrentActiveStock" object:self];
            NSLog(@"isActiveStock? %@",[defaults objectForKey:@"isActiveStockTakeForCurrentStore"]);
            NSLog(@"ActiveStockTakeid? %@",[defaults objectForKey:@"ActiveStockTakeid"]);
            NSLog(@"ActiveStockTakeName? %@",[defaults objectForKey:@"ActiveStockTakeName"]);
            NSLog(@"ActiveStockTakeDate? %@",[defaults objectForKey:@"ActiveStockTakeDate"]);
            [defaults synchronize];
            
            self.fetchedResultsController = nil;
            NSManagedObjectContext *context = [appDelegate managedObjectContext];

            NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"RackStockTakeStatus"];

            [self requestLocationDataWithStatus];
            
            NSError *error = nil;
            
            NSArray *results = [context executeFetchRequest:request error:&error];
            
            if (error != nil) {
                //Deal with failure
            }
            else {
                NSLog(@"Data from Core Data %@", results);
                NSLog(@"Count From Core Data Entity : %d",[results count]);
            }
            [self performFetchForItemsInStore];
            [self.tableView reloadData];
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
       }
    }];
    [operation start];
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    appDelegate.selectedRackForTakeStock = [self.fetchedResultsController objectAtIndexPath:indexPath];
    NSLog(@"Selected Rack %@",appDelegate.selectedRackForTakeStock);
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    [defaults setObject:cell.textLabel.text forKey:@"selectedRackInTakeStock"];
    [defaults synchronize];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    NSLog(@"Number of Rows: %d",[sectionInfo numberOfObjects]);
    return [sectionInfo numberOfObjects];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"Number of Sections: %d",[[self.fetchedResultsController sections] count]);
    return [[self.fetchedResultsController sections] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"takeStockCell";
    
    UITableViewCell * cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
    }
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
    RackStockTakeStatus *rackStockTakeStatus = [self.fetchedResultsController objectAtIndexPath:indexPath];
    UILabel *rackName, *status, *user, *progress;
    
    rackName = (UILabel *)[cell viewWithTag:1];
    rackName.text = rackStockTakeStatus.locName;
    
    status = (UILabel *)[cell viewWithTag:2];
    status.text = rackStockTakeStatus.status;
    status.text = rackStockTakeStatus.status;
    
    progress = (UILabel *)[cell viewWithTag:3];
    if (rackStockTakeStatus.percentCompleted!=nil)
    {
        progress.text = [NSString stringWithFormat:@"Progress: %@%%",rackStockTakeStatus.percentCompleted];
    }
    
    if (rackStockTakeStatus.stockTakeByUser!=nil)
    {
        user = (UILabel *)[cell viewWithTag:4];
        user.text = rackStockTakeStatus.stockTakeByUser;
    }
}

- (void)requestLocationDataWithStatus {
    NSLog(@"Entering requestDataItemsForStore");
    
    NSString *requestPath = [NSString stringWithFormat:@"/stocktake/stocktake/%@/usr/1/locwithstatus",[defaults objectForKey:@"loggedInUserSelectedStoreId"]];

    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:[appDelegate baseUrl]]];
    NSMutableURLRequest *request = [httpClient requestWithMethod:@"GET"
                                                            path:requestPath
                                                      parameters:nil];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSInteger statusCode = operation.response.statusCode;
       // NSLog(@"AFNetowrking Response: %@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
        NSLog(@"AF Status Code %d",statusCode);
        NSError *error;
        NSMutableDictionary *returnedDict = [NSJSONSerialization JSONObjectWithData:responseObject options:kNilOptions error:&error];
        if (statusCode==200)
        {
            for (NSDictionary *rackStockTakeStatus in returnedDict) {
                RackStockTakeStatus *rackStockTakeStatusObj;
                rackStockTakeStatusObj.stockTakeLocId = rackStockTakeStatus[@"stockTakeLocId"];
                rackStockTakeStatusObj.stockTakeUuid = rackStockTakeStatus[@"stockTakeUuid"];
                rackStockTakeStatusObj.locId = rackStockTakeStatus[@"locId"];
                rackStockTakeStatusObj.locName = rackStockTakeStatus[@"locName"];
                rackStockTakeStatusObj.status = rackStockTakeStatus[@"status"];
                rackStockTakeStatusObj.stockTakeByUser = rackStockTakeStatus[@"stockTakeByUser"];
                rackStockTakeStatusObj.stockTakeByUserId = rackStockTakeStatus[@"stockTakeByUserId"];
                rackStockTakeStatusObj.beginTime = rackStockTakeStatus[@"beginTime"];
                [self saveDataToCoreDataEntity:rackStockTakeStatusObj];
            }
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    }];
    [operation start];
}


- (NSFetchedResultsController *)fetchedResultsController {
    
    if (fetchedResultsController != nil) {
        return fetchedResultsController;
    }
    
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    NSSortDescriptor *rackNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"locName" ascending:YES];
    NSArray *sortDescriptors = @[rackNameDescriptor];
    [fetchRequest setSortDescriptors:sortDescriptors];
   fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
    fetchedResultsController.delegate = self;
    return fetchedResultsController;
}

- (void)fetchItemsForStore {
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
   [request setEntity:entity];
    NSError *error;
    [context executeFetchRequest:request error:&error];
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    NSLog(@"Begin Updates");
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    
    UITableView *tableView = self.tableView;
    
    switch(type) {
            
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
            
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
            
        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
            
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
    }
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
            
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
            
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    NSLog(@"End Updates");
    [self.tableView endUpdates];
}

-(void)performFetchForItemsInStore
{
    NSLog(@"Entering performFetchForItemsInStore");
    NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
    }
}

- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSLog(@"Cancelled - searchDisplayControllerDidEndSearch");
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
   [fetchRequest setEntity:entity];
    NSSortDescriptor *rackNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"locName" ascending:YES];
    NSArray *sortDescriptors = @[rackNameDescriptor];
    [fetchRequest setSortDescriptors:sortDescriptors];
    fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
    fetchedResultsController.delegate = self;
    [self performFetchForItemsInStore];
    [self.tableView reloadData];
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    NSInteger searchOption = controller.searchBar.selectedScopeButtonIndex;
    return [self searchDisplayController:controller shouldReloadTableForSearchString:searchString searchScope:searchOption];
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchScope:(NSInteger)searchOption {
    NSString * searchString = controller.searchBar.text;
    return [self searchDisplayController:controller shouldReloadTableForSearchString:searchString searchScope:searchOption];
}

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString*)searchString searchScope:(NSInteger)searchOption {
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"locName contains[cd] %@", searchString];
    [self.fetchedResultsController.fetchRequest setPredicate:predicate];
    [self.fetchedResultsController.fetchRequest setFetchLimit:100];
    
    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    TODO: "Error Handling / Error message";
    }
    return YES;
}

-(void)saveDataToCoreDataEntity:(RackStockTakeStatus *) rackStockTakeStatus
{
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSManagedObject *rackStockTakeStatusManagedObject = [NSEntityDescription insertNewObjectForEntityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeLocId forKey:@"stockTakeLocId"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeUuid forKey:@"stockTakeUuid"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.locId forKey:@"locId"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.locName forKey:@"locName"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.status forKey:@"status"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeByUser forKey:@"stockTakeByUser"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.stockTakeByUserId forKey:@"stockTakeByUserId"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.beginTime forKey:@"beginTime"];
    [rackStockTakeStatusManagedObject setValue:rackStockTakeStatus.percentCompleted forKey:@"percentCompleted"];

    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Save Failed for Data %@! %@ %@", rackStockTakeStatus, error, [error localizedDescription]);
    }
}

当您收到响应时,您会用它填充一个未初始化的 CoreData 对象。

        if (statusCode==200)
        {
            for (NSDictionary *rackStockTakeStatus in returnedDict) {
                RackStockTakeStatus *rackStockTakeStatusObj; // !!! MISSING INITIALIZATION HERE
                rackStockTakeStatusObj.stockTakeLocId = rackStockTakeStatus[@"stockTakeLocId"];

填充的时候可以指向任意一块内存

这种情况的常见做法是获取已存在的 CoreData 对象作为其 ID。如果不存在此类 ID 的对象,则应创建一个新对象。 在你可以用数据填充它之后,获取的结果控制器将观察到变化。

        if (statusCode==200)
        {
            for (NSDictionary *rackStockTakeStatus in returnedDict) {
                NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"RackStockTakeStatus"];
                request.fetchLimit = 1;
                request.predicate = [NSPredicate predicateWithFormat:@"stockTakeLocId = %@", rackStockTakeStatus[@"stockTakeLocId"]];
                NSError *error = nil;
                RackStockTakeStatus *rackStockTakeStatusObj = [[context executeFetchRequest:request error:&error] firstObject];
                if (nil == rackStockTakeStatusObj)
                {
                     rackStockTakeStatusObj = [NSEntityDescription insertNewObjectForEntityForName:@"RackStockTakeStatus" inManagedObjectContext:context]
                     rackStockTakeStatusObj.stockTakeLocId = rackStockTakeStatus[@"stockTakeLocId"];
                }
                ...