Добавление уникальных объектов в базовые данные - PullRequest
10 голосов
/ 06 августа 2009

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

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

Проблема в этом - производительность. Выполнение постоянных запросов к базовым данным происходит медленно, до такой степени, что добавление списка из 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];
}

Ответы [ 3 ]

6 голосов
/ 23 октября 2010

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

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

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

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

4 голосов
/ 14 августа 2009

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

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

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

Несколько модификаций, которые могут помочь ускорить процесс: 1. Получить только выбранные свойства для NSManagedObjects 2. Получить только свойство идентификатора для 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
3 голосов
/ 06 августа 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...