【问题标题】:Core Data optimization核心数据优化
【发布时间】:2012-02-16 23:37:45
【问题描述】:

我在 NSDictionary 中有大量用户,其结构如下:

        97 =             {
            birthday = "";
            gender = Unspecified;
            image =                 {
                status = Prepared;
                type = Image;
            };
            "name_display" = "Facebook User";
            "name_first" = Facebook;
            "name_last" = User;
            type = Contact;
            "user_id" = 97;
        };
        98 =             {
            birthday = "";
            gender = Unspecified;
            image =                 {
                status = Prepared;
                type = Image;
            };
            "name_display" = "Facebook User";
            "name_first" = Facebook;
            "name_last" = User;
            type = Contact;
            "user_id" = 98
         }

我想将此数据输入到 Core Data 中。首先我必须检查用户是否已经存在于核心数据中。如果是这样,请更新该用户。否则创建一个新用户。我这样做的方式有效,但速度非常慢。这是我的代码:

NSDictionary *users = [responseData objectForKey:@"users"];
if (users) {
    for (id userKey in [users allKeys]) {
        NSDictionary *contactDictionary = [users objectForKey:userKey];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"userID == %@", userKey];
        NSUInteger count = [CoreDataHelper countForEntity:@"Contact" withPredicate:predicate andContext:[FSAppDelegate managedObjectContext]];
        if (count > 0) {
            NSMutableArray *existingContactArray = [CoreDataHelper searchObjectsForEntity:@"Contact" withPredicate:predicate andSortKey:nil andSortAscending:NO andContext:[FSAppDelegate managedObjectContext]];
            Contact *existingContact = [existingContactArray objectAtIndex:0];
            [CoreDataHelper updateContactWithDictionary:contactDictionary forContactObject:existingContact];
        }
        else {
            Contact *newContact = (Contact*)[NSEntityDescription insertNewObjectForEntityForName:@"Contact" inManagedObjectContext:[FSAppDelegate managedObjectContext]];
            [CoreDataHelper updateContactWithDictionary:contactDictionary forContactObject:newContact];
        }
    }

    NSError *error;
    if (![[FSAppDelegate managedObjectContext] save:&error]) {
        // Handle the error.
        NSLog(@"error saving to db - fsrequest class.");
    }
}

这是我更新联系人的方法

+(BOOL)updateContactWithDictionary:(NSDictionary*)changedContact forContactObject:(Contact*)contact {
NSString *bday = [changedContact valueForKey:@"birthday"];
NSString *gender = [changedContact valueForKey:@"gender"];
NSString *nameDisplay = [changedContact valueForKey:@"name_display"];
NSString *nameFirst = [changedContact valueForKey:@"name_first"];
NSString *nameLast = [changedContact valueForKey:@"name_last"];
NSString *type = [changedContact valueForKey:@"type"];
NSString *userID = [NSString stringWithFormat:@"%@",[changedContact valueForKey:@"user_id"]];
NSString *imageStatus = [[changedContact objectForKey:@"image"]objectForKey:@"status"];
NSString *imageType = [[changedContact objectForKey:@"image"]objectForKey:@"type"];
NSString *imageURL = [[changedContact objectForKey:@"image"]objectForKey:@"url"];
NSString *imageThumb = [[changedContact objectForKey:@"image"]objectForKey:@"url_thumb"];
NSString *locationName = [[changedContact objectForKey:@"location"]objectForKey:@"name"];

[contact setBirthday:bday];
[contact setGender:gender];
[contact setNameDisplay:nameDisplay];
[contact setNameFirst:nameFirst];
[contact setNameLast:nameLast];
[contact setType:type];
[contact setUserID:userID];
[contact setImageStatus:imageStatus];
[contact setImageType:imageType];
if (imageURL && !((NSNull *)imageURL == [NSNull null])) {
    [contact setImageURL:imageURL];
}
if (imageThumb && !((NSNull *)imageThumb == [NSNull null])) {
    [contact setImageThumbURL:imageThumb];
}
if (locationName && !((NSNull *)locationName == [NSNull null])) {
    [contact setLocationName:locationName];
}
return YES;
}

谁能给我一个例子,说明我将如何以更快的方式做到这一点?有些人提到了一些想法,但我需要看到它才能理解。谢谢!

【问题讨论】:

  • 您的 userID 属性是否在您的数据模型中标记为已编入索引?
  • 不,你这是什么意思?
  • 在数据模型检查器中,您可以将实体的属性标记为已索引(在将属性标记为临时或可选的同一部分中),这意味着将在底层 SQLite 数据库。这应该会提高您的性能,因为您的提取是基于该属性的。
  • 这里有一个基本解释的问题:stackoverflow.com/questions/7935348/…

标签: iphone ios optimization core-data


【解决方案1】:

您需要了解获取请求的成本很高(它需要运行 SQL 并执行 I/O)。我不确定你的CoreDataHelper 来自哪里,但它会做某种昂贵的NSFetchRequest。您可以构造一个NSFetchRequest,它会告诉您哪些对象已经存在。这会将成本从 O(N) 降低到 O(1)。

[request setPredicate:[NSPredicate predicateWithFormat:@"userID IN %@", allKeys]];

并且,如上所述,将保存移出循环。 但是如果您要更新添加 许多 个对象,您可能需要不时保存。

【讨论】:

  • 什么是“许多对象”?我会说大约 1,000 是我正在更新和添加的内容。
  • 您必须进行测试——这取决于。看看每 100 次保存是否会使其更快或更慢。
【解决方案2】:

首先,我将 save: 移到循环之外。替换:

 // save core data
        NSError *error;
        if (![[FSAppDelegate managedObjectContext] save:&error]) {
            // Handle the error.
            NSLog(@"error saving to db - fsrequest class.");
        }
    }
}

    }
    // save core data
    NSError *error;
    if (![[FSAppDelegate managedObjectContext] save:&error]) {
    // Handle the error.
    NSLog(@"error saving to db - fsrequest class.");
  }
}

另外,你有一些在 Core Data 模型中定义的 imageURL、imageThumb 和 locationName 的默认值吗?如果不是,为什么要检查空值(两次)?

奖励:

消除 countForEntity:withPredicate:andContext: 调用可能是个好主意,如下所示:

NSMutableArray *existingContactArray = [CoreDataHelper searchObjectsForEntity:@"Contact" withPredicate:predicate andSortKey:nil andSortAscending:NO andContext:[FSAppDelegate managedObjectContext]];

if ([existingContactArray count] > 0)
{
    Contact *existingContact = [existingContactArray objectAtIndex:0];

    [CoreDataHelper updateContactWithDictionary:contactDictionary forContactObject:existingContact];
}

【讨论】:

  • 我再次编辑了我的答案,以展示我如何使用快速枚举。我认为它可以更快。我检查 imageURL 和 imageThumb 是否为 null,因为有时服务器会发送 null,但我想保留我在那里的所有内容,而不是将其更新为 null。
  • 您消除 countForEntity 的想法是个好主意,但我的实体计数方法在没有实际查询所有对象的情况下进行计数。这样更快。
  • 如果您在每条插入记录后不再保存并且仍然遇到性能问题,那么我看不出您如何能大大加快速度。确保您没有在 fetch 例程 (searchObjectsForEntity) 中做任何不必要的事情。你也可以尝试批量保存:调用(见this answer)。
猜你喜欢
  • 2011-10-05
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
  • 2023-04-06
相关资源
最近更新 更多