Добавление уникальных объектов в базовые данные

Я работаю над iPhone-приложением, которое получает несколько объектов из базы данных. Я хотел бы хранить их с помощью Core Data, но у меня проблемы с моими отношениями.

A Деталь содержит любое количество POI (точки интереса). Когда я выбираю набор POI с сервера, они содержат идентификатор детали. Чтобы связать POI с деталями (по идентификатору), мой процесс выглядит следующим образом: Запросите ManagedObjectContext для идентификатора detailID. Если эта деталь существует, добавьте poi к ней. Если это не так, создайте деталь (у нее есть другие свойства, которые будут заполнены лениво).

Проблема с этим - производительность. Выполнение постоянных запросов к Core Data происходит медленно, до такой степени, что добавление списка из 150 POI занимает минуту благодаря нескольким связанным отношениям.

В моей старой модели перед Core Data (различные объекты NSDictionary) этот процесс был очень быстрым (найдите ключ в словаре, а затем создайте его, если он не существует)

У меня больше отношений, чем только этот, но в значительной степени каждый должен сделать эту проверку (некоторые из многих для многих, и у них есть настоящая проблема).

Есть ли у кого-нибудь предложения, как я могу это помочь? Я мог выполнять меньше запросов (путем поиска нескольких идентификаторов), но я не уверен, насколько это поможет.

Некоторые коды:

        POI *poi = [NSEntityDescription
                insertNewObjectForEntityForName:@"POI"
                inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];

    poi.POIid = [attributeDict objectForKey:kAttributeID];
    poi.detailId = [attributeDict objectForKey:kAttributeDetailID];
    Detail *detail = [self findDetailForID:poi.POIid];
    if(detail == nil)
    {
        detail = [NSEntityDescription
                    insertNewObjectForEntityForName:@"Detail"
                    inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
        detail.title = poi.POIid;
        detail.subtitle = @"";
        detail.detailType = [attributeDict objectForKey:kAttributeType];
    }





-(Detail*)findDetailForID:(NSString*)detailID {
NSManagedObjectContext *moc = [[UIApplication sharedApplication].delegate managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
                                          entityForName:@"Detail" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

NSPredicate *predicate = [NSPredicate predicateWithFormat:
                          @"detailid == %@", detailID];
[request setPredicate:predicate];
NSLog(@"%@", [predicate description]);

NSError *error;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil || [array count] != 1)
{
        // Deal with error...
    return nil;
}
return [array objectAtIndex:0];
}

Ответ 1

Эта страница дает некоторую помощь по оптимизации производительности: http://developer.apple.com/documentation/Cocoa/Conceptual/CoreData/Articles/cdPerformance.html#//apple_ref/doc/uid/TP40003468-SW1

Не очень эффективно, почему бы просто не построить их в памяти с помощью NSDictionary? Прочитайте все из Core Data в NSDictionary, затем объедините свои данные, заменив все в Core Data.

Ответ 2

Ознакомьтесь с разделом "Batch Faulting" на странице под названием "Производительность основных данных" в Руководстве по программированию основных данных Xcode, на которое ссылается Норман в своем ответе.

Только получение этих управляемых объектов, идентификаторами которых являются IN, может быть еще более эффективна коллекция (NSSet, NSArray, NSDictionary) идентификаторов объектов, возвращаемых сервером.

NSSet *oids = [[NSSet alloc] initWithObjects:@"oid1", @"oid2", ..., nil];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"oid IN %@", oids];
[oids release];

UPDATE: я разработал этот совет для решения acani usersView. В принципе, после загрузки ответа JSON пользователей, iPhone использует популярную среду JSON с открытым исходным кодом для анализа ответ в NSArray объектов NSDictionary, каждый из которых представляет пользователя. Затем он делает NSArray своих uids и выполняет пакетную выборку на Core Data, чтобы узнать, существует ли какая-либо из них на iPhone. Если нет, он вставляет его. Если это так, он обновляет те, которые существуют, только если их атрибут updated старше, чем тот, который находится на сервере.

Ответ 3

Я получил все, чтобы работать очень хорошо, благодаря Норманну, который поставил меня на правильный путь. Я отправлю свой вспомогательный класс для других.

В принципе, мой вспомогательный класс будет искать, если NSManagedObject существует для некоторого ID и может создать его для некоторого ID. Это выполняется достаточно быстро для меня, с 1000 операций поиска/создания, занимающих около 2 секунд на моем iPhone (я также сделал несколько других вещей там, чистая находка/создание, скорее всего, быстрее).

Это делается путем кэширования словаря всех NSManagedObjects и проверки этого кеша, а не выполнения нового NSFetchRequest.

Несколько модификаций, которые могут ускорить работу: 1. Получить только выбранные свойства для NSManagedObjects 2. Получите свойство identifier для NSManagedObject в словаре, а не весь объект. В моем тестировании производительности единственный запрос не был медленной частью (но только с 1000 элементами, я бы ожидал, что это будет быстро). Медленной частью было создание элементов.

  #import "CoreDataUniquer.h"


@implementation CoreDataUniquer

    //the identifying property is the field on the NSManagedObject that will be used to look up our custom identifier
-(id)initWithEntityName:(NSString*)newEntityName andIdentifyingProperty:(NSString*)newIdProp
{
    self = [super init];
    if (self != nil) {
        entityName = [newEntityName retain];
        identifyingProperty = [newIdProp retain];
    }
    return self;
}

-(NSManagedObject*)findObjectForID:(NSString*)identifier
{
    if(identifier == nil)
    {
        return nil;
    }
    if(!objectList)
    {   
        NSManagedObjectContext *moc = [(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext];
        NSEntityDescription *entityDescription = [NSEntityDescription
                                                  entityForName:entityName inManagedObjectContext:moc];
        NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
        [request setEntity:entityDescription];

        NSError *error;
        NSArray *array = [moc executeFetchRequest:request error:&error];
        objectList = [[NSMutableDictionary dictionary] retain];
        for (NSManagedObject* p in array) {
            NSString* itemId = [p valueForKey:identifyingProperty];
            [objectList setObject:p forKey:itemId];
        }
    }
    NSManagedObject* returnedObject = [objectList objectForKey:identifier];
    return returnedObject;
}
-(NSManagedObject*)createObjectForID:(NSString*)identifier
{

    NSManagedObject* returnedObject = [NSEntityDescription
                                       insertNewObjectForEntityForName:entityName
                                       inManagedObjectContext:[(AppDelegate*)[UIApplication sharedApplication].delegate managedObjectContext]];
    [returnedObject setValue:identifier forKey:identifyingProperty];
    [objectList setObject:returnedObject forKey:identifier];
    return returnedObject;
}


- (void) dealloc
{
    DESTROY(entityName);
    DESTROY(identifyingProperty);
    [super dealloc];
}

@end