后台线程上的核心数据获取请求仅适用于第一次获取

时间:2013-03-07 01:50:46

标签: ios xcode core-data nsmanagedobject nsoperationqueue

我正在使用NSOperationQueue& NSInvocationOperation在我的UIViewController上调用一个方法,然后调用另一个类中的方法,该方法又从Web解析XML文件并更新一些Core Data Managed Objects。

当我首先运行应用程序时,此过程有效,但是当我第二次调用该方法时(再次显示UIViewController时),它不会在XML解析器类中的executeFetchRequest语句之后执行任何行。这是我用来执行的代码:

我有一个UITableViewController,它在加载时从我的Core Data商店获取停车场的所有静态信息:

- (void)viewDidLoad
{
    NSLog(@"viewDidLoad");
    [super viewDidLoad];

    southeastCarparks = [[NSMutableArray alloc] init];
    southwestCarparks = [[NSMutableArray alloc] init];
    northeastCarparks = [[NSMutableArray alloc] init];
    northwestCarparks = [[NSMutableArray alloc] init];
    carparkLocations = [[NSMutableArray alloc] initWithObjects:southeastCarparks, southwestCarparks, northeastCarparks, northwestCarparks, nil];


    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"CarparkInfo" inManagedObjectContext:self.managedObjectContext];


    [fetchRequest setEntity:entity];
    NSError *error;
    self.carparkInfos = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

    for (CarparkInfo *carpark in self.carparkInfos){
        if ([carpark.details.region isEqualToString:@"Southeast"]){
            [southeastCarparks addObject:carpark];
        } else if ([carpark.details.region isEqualToString:@"Southwest"]){
            [southwestCarparks addObject:carpark];
        } else if ([carpark.details.region isEqualToString:@"Northeast"]){
            [northeastCarparks addObject:carpark];
        } else if ([carpark.details.region isEqualToString:@"Northwest"]){
            [northwestCarparks addObject:carpark];
        }
    }

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(loadXMLData)
                                             name:@"appDidBecomeActive"
                                           object:nil];
}

然后当视图出现时,我调用loadXML方法转到Web并从xml文件中获取最新的停车位信息

- (void) viewDidAppear:(BOOL)animated
{
    [self loadXMLData];
    NSLog(@"viewDidAppear");
}


- (void) loadXMLData {
    NSLog(@"loadXMLData");

    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                        selector:@selector(loadXMLDataWithOperation)
                                                                          object:nil];
    [queue addOperation:operation];
}

- (void) loadXMLDataWithOperation {
    xmlParser = [[XMLParser alloc] loadXMLByURL:xmlFileURL];

    [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];
}

在XMLParser类中,我接受传入的xmlFileURL,从Web检索它,解析它然后尝试更新Core Data存储中的可用空间

-(id) loadXMLByURL:(NSString *)urlString
{
    _carparks = [[NSMutableArray alloc] init];
    NSURL *url = [NSURL URLWithString:urlString];
    NSData *data = [[NSData alloc] initWithContentsOfURL:url];
    parser = [[NSXMLParser alloc] initWithData:data];
    parser.delegate = self;
    [parser parse];
    return self;
}

- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if (![elementName isEqualToString:@"carpark"]){
        return;
    }

    currentCarpark = [Carpark alloc];

    NSString *name = [attributeDict objectForKey:@"name"];
    currentCarpark.name = name;

    NSString *spaces = [attributeDict objectForKey:@"spaces"];
    currentCarpark.spaces = spaces;

    [self.carparks addObject:currentCarpark];
    currentCarpark = nil; 

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    // set up the managedObjectContext to read data from CoreData
    id delegate = [[UIApplication sharedApplication] delegate];
    self.managedObjectContext = [delegate managedObjectContext];

    NSEntityDescription *entity = [NSEntityDescription
                               entityForName:@"CarparkInfo" inManagedObjectContext:self.managedObjectContext];

    [fetchRequest setEntity:entity];
    [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"code=%@",name]];

    NSError *error;
    CarparkInfo *cgCarpark;

    // THIS IS THE LAST LINE TO BE EXECUTED THE 2ND TIME AROUND. THE APPLICATION DOESN'T THROW AN ERROR BUT DOESN'T GO ON TO UPDATE THE CARPARK OBJECT ON THE LINE AFTER
    cgCarpark = [[self.managedObjectContext executeFetchRequest:fetchRequest error:&error] lastObject];

    cgCarpark.availableSpaces = spaces;
    error = nil;
    if (![self.managedObjectContext save:&error]) {
        //Handle any error with the saving of the context
    }
}

我在使用托管对象上下文的方式上做错了吗?

提前感谢您提供任何帮助

2 个答案:

答案 0 :(得分:3)

我认为@Mark_Kryzhanouski是正确的,你有一个僵局。可能是因为您在多个线程上访问NSManagedObjectContext。你需要小心Core data中的线程。您的NSOperation将在后台线程上运行,因此对NSManagedObjectContext的调用与viewDidLoad中第一次调用的线程不同。以下是Core Data并发的一些参考资料......

Concurrency with Core DataCore Data Release Notes for OS X v10.7 and iOS 5.0

WWDC 2012视频中有关于该主题的WWDC演示文稿,“会议214 - 核心数据最佳实践”。

答案 1 :(得分:1)

在获取结果控制器之前,只需编写以下语句

self.fetchedResultsController = nil;