来自多个类和线程的异步核心数据

时间:2014-02-17 10:34:09

标签: ios objective-c multithreading core-data asynchronous

在尝试弄清楚我之前的问题后,究竟是什么问题: fetchedObjects (NSArray) count return 0 when it's full of objects 我非常确定我需要我的核心数据来自多个类和线程的异步。 我尝试连续多次调用我的核心数据,我没有问题。 但显然我需要它来自多个类和线程的读/写。 我使用@synchronized并且仍然没有,我在核心数据的fetchedObjects数组中有0条记录,但那里有数据。

这样做的正确方法是什么?

编辑1:

如果我尝试使用NSTimer安排它,上面的代码只能运行一次:

TrapService.mm:

self.managedObjectContext = appDelegate.managedObjectContext;
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    NSError *error = nil;
    NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    NSLog(@"fetchedObjects.count: %d", fetchedObjects.count);

编辑2:

我使用核心数据的代码的另一个例子,执行一次,然后所有关于核心数据的操作什么也不做,或者给我带有0条记录的数组。

TrapService.mm:

- (void)initializeQuadTree
{
    self.qTree = [[QuadTree alloc] init];
    self.qTree = [dbat addCoordinatesToQuadTree:self.qTree];
}

- (Traps*)getCloseTrapFromTree:(CLLocation*)location
{
    return [dbat getCloseTrapFromTree:self.qTree andLocation:location];
}

DataBaseAllTraps.m:

- (QuadTree*)addCoordinatesToQuadTree:(QuadTree*)quadTree
{
    if (quadTree == nil) {
        quadTree = [[QuadTree alloc] init];
    }

    BOOL success = YES;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSError *error = nil;
    NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil || fetchedObjects.count == 0)
    {
        NSLog(@"addCoordinatesToQuadTree - localizedDescription: %@, userInfo: %@", error.localizedDescription, error.userInfo);
        success = NO;
    }

    NSLog(@"addCoordinatesToQuadTree - fetchedObjects.count: %d", fetchedObjects.count);

    if (success)
    {
        for (CoreDataAllTraps *trap in fetchedObjects)
        {
            double latitude = trap.lat.doubleValue;
            double longitude = trap.lon.doubleValue;

            double closePointLat = trap.close_point_lat.doubleValue;
            double closePointLon = trap.close_point_lon.doubleValue;

            DummyAnnotation *trapAnnotation = [[DummyAnnotation alloc] init];

            if (closePointLat != 0.0 || closePointLon != 0.0) trapAnnotation.coordinate = CLLocationCoordinate2DMake(closePointLat, closePointLon);
            else trapAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);

            [quadTree insertObject:trapAnnotation];
        }
    }
    else
    {
        for (Traps *trap in kNETROADS_CONTEXT.arrayOfAllTraps)
        {
            double latitude = trap.lat;
            double longitude = trap.lon;

            double closePointLat = trap.closePointLat;
            double closePointLon = trap.closePointLon;

            DummyAnnotation *trapAnnotation = [[DummyAnnotation alloc] init];

            if (closePointLat != 0.0 || closePointLon != 0.0) trapAnnotation.coordinate = CLLocationCoordinate2DMake(closePointLat, closePointLon);
            else trapAnnotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);

            [quadTree insertObject:trapAnnotation];
        }
    }
    NSLog(@"TOTAL NUMBER OF TRAPS (%s): %i", __PRETTY_FUNCTION__, success?fetchedObjects.count:[Netroads sharedInstance].arrayOfAllTraps.count);
    return quadTree;
}

- (Traps*)getCloseTrapFromTree:(QuadTree*)quadTree andLocation:(CLLocation*)location
{
    NSLog(@"%s", __PRETTY_FUNCTION__);

    NSArray *closeTraps = [quadTree neighboursForLocation:location.coordinate limitCount:1];

    if (closeTraps.count == 0) { return nil; }

    // NSAssert(closeTraps.count > 0, @"closeTraps.count == 0, get close trap from quad tree.");

    int trapID = 0;
    DummyAnnotation *trapLocation = closeTraps.firstObject;

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == %f AND %@ == %f", CLOSE_POINT_LAT, trapLocation.coordinate.latitude, CLOSE_POINT_LON, trapLocation.coordinate.longitude];
    [fetchRequest setEntity:entity];
    [fetchRequest setPredicate:predicate];
    [fetchRequest setFetchLimit:1];

    NSError *error = nil;
    NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

    if (fetchedObjects != nil && fetchedObjects.count > 0) { // We have close point
        CoreDataAllTraps *trap = fetchedObjects.firstObject;
        trapID = trap.trapID.intValue;
    }
    else { // We do not have close point, use normal coordinates (lat, lon)
        NSLog(@"%s error: %@\n%@", __PRETTY_FUNCTION__, error.localizedDescription, error.userInfo);

        fetchRequest = [[NSFetchRequest alloc] init];
        entity = [NSEntityDescription entityForName:kCORE_DATA_ALL_TRAPS_ENTITY inManagedObjectContext:self.managedObjectContext];
        predicate = [NSPredicate predicateWithFormat:@"%@ == %f AND %@ == %f", LAT, trapLocation.coordinate.latitude, LON, trapLocation.coordinate.longitude];
        [fetchRequest setEntity:entity];
        [fetchRequest setPredicate:predicate];
        [fetchRequest setFetchLimit:1];

        error = nil;
        fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

        if (fetchedObjects != nil && fetchedObjects.count > 0) {
            CoreDataAllTraps *trap = fetchedObjects.firstObject;
            trapID = trap.trapID.intValue;
        }
        else {
            NSLog(@"%s error: %@\n%@", __PRETTY_FUNCTION__, error.localizedDescription, error.userInfo);
        }
    }

    if (trapID > 0) {
        return [self getTrap_trapID:trapID];
    }
    else {
        return nil;
    }
}

编辑3:

我正在创建一个新的MOC,但仍然没有,同样的问题:

NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator];

2 个答案:

答案 0 :(得分:2)

我没有分析你的代码。 (我太懒了。:-))但是当我搜索一次保存时,我找不到。

请记住,发生了什么:在标准设置中,您有一个SQL-DB作为后端。每个线程/队列都有不同的上下文,它们(部分地)在获取请求中取出SQL-DB的数据,并(部分地)将其保存在保存请求中。

没有上下文将其更改(包括插入和删除)自动推送到数据库或其他上下文。没有上下文从DB或其他上下文中自动推送另一个上下文推送的更改。因此,必须以“手动”方式将数据从上下文传输到另一个数据。

只要你没有删除,只需在使用save完成一个上下文时存储数据,并在另一个线程上监听did save通知。

答案 1 :(得分:1)

阅读有关如何使用CoreData in a concurrent fashion的Apples文档。

基本上,每个线程使用单独的NSManagedObjectContext并且不在这些线程之间传递对象非常重要,但只能通过NSManagedObjectID引用它们。

上面的代码示例需要有关您起诉该代码的位置的更多信息。但令我惊讶的是

self.managedObjectContext = appDelegate.managedObjectContext;

如果没有在主线程上运行,这与并发指南要做的完全相反。使用该行,您只需创建指向appDelegate.managedObjectContext的指针。这不是一个新对象!

如果以正确的方式完成,通常不需要同步或添加锁等。

要给出一个好的答案,虽然你的问题太模糊,但需要一个相当冗长的答案。但也许在阅读Apple的文档后,您可以部分解决您的问题并回过头来解决问题。这可以更容易地得到令人满意的回答。