OStack程序员社区-中国程序员成长平台

标题: ios - 后台线程上的核心数据获取请求仅适用于首次获取 [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-13 01:25
标题: ios - 后台线程上的核心数据获取请求仅适用于首次获取

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

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

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

- (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
                                         selectorselector(loadXMLData)
                                             name"appDidBecomeActive"
                                           object:nil];
}

然后,当 View 出现时,我调用 loadXML 方法转到 web 并从 xml 文件中获取最新的 parking 位信息

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


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

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

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

    [self.tableView performSelectorOnMainThreadselector(reloadData) withObject:nil waitUntilDone:YES];
}

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

-(id) loadXMLByURLNSString *)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) parserNSXMLParser *)parser didStartElementNSString *)elementName namespaceURINSString *)namespaceURI qualifiedNameNSString *)qName attributesNSDictionary *)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
    }
}

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

提前感谢您的帮助



Best Answer-推荐答案


我认为@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 视频“Session 214 - Core Data Best Practices”中也有关于该主题的精彩 WWDC 演示。

关于ios - 后台线程上的核心数据获取请求仅适用于首次获取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15261648/






欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) Powered by Discuz! X3.4