2009-08-05 3 views
10

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

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

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

В моей старой модели до основных данных (различные 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

Объясните свои объекты более подробно и разместите часть кода, который вы используете. Почти на 100%, вероятно, есть способ сделать то, что вы уже делаете намного быстрее. – Sneakyness

ответ

3

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

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

+0

Интересная идея. Тогда шаблон будет при создании объектов 1. Извлеките возможный диапазон связанных объектов, сохраните в NSDictionary с ключом 2. Если идентификатор существует, добавьте отношение к этому объекту 3. Если идентификатор отсутствует, добавьте новый объект как для словаря, так и для основных данных. Добавьте отношения. – Dimitri

4

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

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

Он делает это путем кэширования словаря всех NSManagedObjects и проверки этого кеша, а не выполнения нового 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 
+0

Кроме того, убедитесь, что вы отметили опцию «индекс» в свойстве идентификатора. – Dimitri

+2

Спасибо! Не могли бы вы также разместить файл .h? – ma11hew28

+0

Вы пытаетесь найти сущность с значением атрибута, проверив все сущности из хранилища. Вы получаете доступ ко всем этим требованиям, и это может привести к изменению состояния ошибки в реальном запросе. Я думаю, что u должен использовать предикат и ограничение для NSFetchRequest и выполнить запрос. если count> 0, это означает, что сущность с атрибутом уже существует в постоянном хранилище. После этого вы можете получить доступ к этому запросу для обновления вашего объекта. –

6

Заканчивать раздел под названием «Пакетное Сбойное» на странице под названием «Core Data Performance» в Руководстве по программированию Core Data Xcode в, что Норман связан в своем ответе.

только те выборки managedObjects, идентификаторы которых 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 response of users iPhone использует popular open source JSON framework для анализа ответа в NSArray объектов NSDictionary, каждый из которых представляет пользователя. Затем он делает NSArray своих uids и делает пакетную выборку на Core Data, чтобы узнать, существует ли какая-либо из них на iPhone. Если нет, он вставляет его. Если это так, он обновляет те, которые существуют, только если их атрибут updated старше, чем атрибут одного из серверов.

Смежные вопросы