【问题标题】:Core data save takes too long核心数据保存时间太长
【发布时间】:2014-07-27 13:11:51
【问题描述】:

我在这里寻找解决我的问题的方法,例如:

Core Data Saves and UI Performance

Why does Core Data take so long to save an object?

很多人都有我的问题,但我尝试使用他们的解决方案没有成功。

这是我的问题:

我正在尝试在后台的 Core Data 中保存 10 个托管对象。我没有任何错误,一切都很好,但是......保存 10 个对象需要 13 秒!我不知道我做错了什么......

我需要在我的数据库中保存 100 个对象,如果只有 10 个对象需要 13 秒...当我尝试保存 100 个时,我真的很害怕会发生什么。

这是我的代码:

首先,我在后台调用我的保存函数:

[self performSelectorInBackground:@selector(updateMessageList:) withObject:response];

我在这里保存对象:

- (void)updateMessageList:(NSArray *)messageList
{
    AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
    NSManagedObjectContext *tmpContext = [[NSManagedObjectContext alloc]init];
    tmpContext.persistentStoreCoordinator = [app persistentStoreCoordinator];

    NSError *error;

    int i = 0; //Counter to log how long it takes to insert a managedObject.

    for (MessageReadResponse* messageUpdate in messageList) {    
        NSLog(@"%d", i++);

        Message *message = [NSEntityDescription insertNewObjectForEntityForName:MESSAGE_ENTITY
                                                         inManagedObjectContext:tmpContext];
        [message setState:STATE_ACTIVE];
        [message setFromName:[[messageUpdate from]name]];
        [message setFromSurname:[[messageUpdate from]surname]];
        [message setBody:[messageUpdate body]];
    }

    NSLog(@"Before saving");

    if (![tmpContext save:&error]) {
        ErrorLog(@"%@", error.description);
        [ErrorManagement synchronizeApplication];

        return;
    }

    NSLog(@"After saving");
}

这是我在主线程中合并 managedObjectContext 的地方:

- (void)manageDidSaveNotification:(NSNotification *)notification
{
    NSManagedObjectContext *savedContext = [notification object];

    // Ignore change notifications for the main MOC
    if (__managedObjectContext == savedContext)
    {
        return;
    }

    if (__managedObjectContext.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)
    {
        // That's another database
        return;
    }

    dispatch_sync(dispatch_get_main_queue(), ^{
        [__managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
    });
}

这是我的性能日志:

2014-06-06 11:12:07.207 app-ios-client[260:8017] 0

2014-06-06 11:12:07.210 app-ios-client[260:8017] 1

2014-06-06 11:12:07.217 app-ios-client[260:8017] 2

2014-06-06 11:12:07.219 app-ios-client[260:8017] 3

2014-06-06 11:12:07.221 app-ios-client[260:8017] 4

2014-06-06 11:12:07.255 app-ios-client[260:8017] 5

2014-06-06 11:12:07.257 app-ios-client[260:8017] 6

2014-06-06 11:12:07.259 app-ios-client[260:8017] 7

2014-06-06 11:12:07.289 app-ios-client[260:8017] 8

2014-06-06 11:12:07.293 app-ios-client[260:8017] 9

2014-06-06 11:12:07.304 app-ios-client[260:8017] Before saving

2014-06-06 11:12:20.900 app-ios-client[260:8017] After saving

非常感谢!

已编辑:

Here 是我的仪器跟踪。

编辑 2:

感谢 Marcus Zarra,我发现这种方法会使我的应用程序运行得太慢:

-(CGFloat)getLabelHeightForIndex:(NSInteger)index
{
    NSIndexPath *path = [NSIndexPath indexPathForRow:0 inSection:index];

    UITextView *gettingSizeLabel = [[UITextView alloc] init];
    gettingSizeLabel.font = [UIFont fontWithName:@"Helvetica" size:FONT_SIZE];
    gettingSizeLabel.text = [[_fetchedResultsController objectAtIndexPath:path]body];

    Message *info = [_fetchedResultsController objectAtIndexPath:path];
    if ([[info contentSize]isEqualToString:@"(null)"]) {
        CGSize maximumLabelSize = CGSizeMake(LABEL_WIDTH_NO_MEDIA, LABEL_TEXT_MAX_HEIGHT);

        CGSize expectedSize = [gettingSizeLabel sizeThatFits:maximumLabelSize];

        if (expectedSize.height < LABEL_TEXT_MIN_HEIGHT_NO_MEDIA) {
            return LABEL_TEXT_MIN_HEIGHT_NO_MEDIA;
        }

        return  expectedSize.height;
    } else {
        CGSize maximumLabelSize = CGSizeMake(LABEL_WIDTH_MEDIA, LABEL_TEXT_MAX_HEIGHT);

        CGSize expectedSize = [gettingSizeLabel sizeThatFits:maximumLabelSize];

        if (expectedSize.height < LABEL_TEXT_MIN_HEIGHT_MEDIA) {
            return LABEL_TEXT_MIN_HEIGHT_MEDIA;
        }

        return  expectedSize.height;
    }
}

这个方法我以前知道单元格的高度,没有它,应用程序运行得非常快。我正在考虑如何改变这种方法以更快地工作......

非常感谢马库斯,真的!

【问题讨论】:

  • 我想如果你在主线程中初始化你的 NSManagedObjectContext,你应该只在主线程中使用它。 Prove
  • -updateMessageList 从后台线程调用,然后 NSManagedObjectContext 在那里初始化。代码是正确的,尽管现在已经过时了。

标签: ios xcode core-data


【解决方案1】:

这些物体有多大?您要保存多少数据?

您使用的是哪种类型的持久性存储? SQLite?

保存后,您的用户界面如何处理这些数据?您是否正在对数据进行任何类型的解析或数据显示?

仪器是怎么说的?它说时间都花在了哪里?

这是第一个问题。

Core Data 绝不应该花那么长时间来保存数据。我怀疑您在用户界面中执行的操作是由真正需要时间的保存触发的。 Instruments 中的 Time Profiler 会告诉我们发生了什么。

虽然我不会按照您的方式编写此代码,但您发布的代码没有任何问题,因此问题一定出在其他地方。仪器会告诉我们去哪里寻找。

更新

感谢您发布跟踪,它证实了我的怀疑。如果您查看时间花费在哪里,则 54% 用于您的NSFetchedResultsController,而 20.6% 用于您的TimelineViewController。这意味着保存速度非常很快,然后您的用户界面对更改的反应非常缓慢。关闭合并更改通知以确认。从那里我建议在时间分析器中深入挖掘您的 UI,并找出它为什么如此慢的原因。

看起来您正在执行一些复杂的字符串操作和其他占用大量 CPU 资源的事情。修复这些,速度就会恢复。

【讨论】:

  • 当我在 UITableView 中使用 UIRefreshControl 执行“下拉刷新”时会调用该代码。在主线程中,当我执行“拉动刷新”时,我会在我的服务器中调用一个 REST 服务,当我收到响应时,我会将其保存在后台并发布代码。我会用仪器检查它。谢谢
  • 这没有回答我发布的任何问题。但是,发布您的仪器配置文件结果,我们会看到时间都花在了哪里。
  • - 这些对象不大。在那一刻,我试图保存 10 个对象,但在此之前我已经保存了数千个对象(没有性能问题)。是的,我正在使用 SQlite。保存后,我的 UI 通过 FetchedResultsControllerDelegate 刷新 UITableView(在该表中,单元格是自定义单元格,带有“气泡”以显示消息)。我编辑了我的帖子以添加仪器跟踪。谢谢
  • 在我的例子中,NSDateFormatter 正在杀死主线程。正如@MarcusS.Zarra 所说,深入挖掘!
  • 马库斯,你是对的。如果没有合并更改通知,该应用程序的运行速度非常快......我将检查我的 UI 中的所有内容,然后,当我发现错误时,我将使用解决方案再次编辑我的帖子。非常感谢!!
【解决方案2】:

首先,如果您总是将这么多的对象保存到 Core Data,请不要为多线程而烦恼。这不会对您的 UI 性能产生太大影响。尝试在主线程中实现这一点,就像任何基本教程告诉你的那样。

如果你真的很想要这个多线程的东西,试试看这个tutorial。您会看到主要的 NSManagedObjectContext 用于您的 UI,任何其他自定义上下文都应该用于处理数据。

对我来说,你正在尝试做一些太好的事情。有时最简单的解决方案是最好的!

【讨论】:

  • 他写的代码没有问题。他正在创建一个线程受限的上下文,写入它并在一个方法中保存所有内容。
猜你喜欢
  • 2017-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多