2

我正在使用 NSOperationQueue 和 NSInvocationOperation 在我的 UIViewController 上调用一个方法,然后该方法调用另一个类中的一个方法,该方法反过来解析来自 Web 的 XML 文件并更新一些核心数据托管对象。

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

我有一个 UITableViewController,它在加载时从我的核心数据存储中获取停车场的所有静态信息:

- (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 方法访问网络并从 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
    }
}

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

提前感谢您的帮助

4

2 回答 2

3

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

OS X v10.7 和 iOS 5.0的 Core Data和Core Data 发行说明的并发性

在 WWDC 2012 视频“Session 214 - Core Data Best Practices”中也有一个很好的 WWDC 演示文稿。

于 2013-03-07T19:54:37.493 回答
1

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

self.fetchedResultsController = nil;
于 2013-03-07T04:52:06.437 回答