2

NSOperationQueueとNSInvocationOperationを使用してUIViewControllerのメソッドを呼び出し、UIViewControllerが別のクラスのメソッドを呼び出して、WebからXMLファイルを解析し、いくつかのCoreData管理対象オブジェクトを更新しています。

このプロセスは、最初にアプリケーションを実行したときに機能しますが、2回目にメソッドを呼び出すと(UIViewControllerが再度表示されたとき)、XMLパーサークラスのexecuteFetchRequestステートメントの後に行が実行されません。実行に使用しているコードは次のとおりです。

ロード時にCoreDataストアから駐車場のすべての静的情報を取得する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メソッドを呼び出して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から取得して解析し、CoreDataストアで使用可能なスペースを更新しようとします。

-(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並行性に関する参考資料を次に示します...

OSXv10.7およびiOS5.0のCoreDataおよびCoreDataリリースノートとの同時実行性

WWDC2012のビデオ「セッション214-コアデータのベストプラクティス」のトピックに関する優れたWWDCプレゼンテーションもあります。

于 2013-03-07T19:54:37.493 に答える
1

結果コントローラーをフェッチする前に、次のステートメントを記述してください

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