Objective c 后台托管对象上下文交错UI动画

Objective c 后台托管对象上下文交错UI动画,objective-c,ios,core-data,user-interface,nsmanagedobjectcontext,Objective C,Ios,Core Data,User Interface,Nsmanagedobjectcontext,我有一个问题,我已经研究了几个星期了。每当我保存我的核心数据管理对象上下文时,它都会影响UI性能。我已经尽我所能,并且正在寻求帮助 形势 我的应用程序使用两个NSManagedObjectContext实例。其中一个属于应用程序委托,并附加了一个持久存储协调器。另一个是主MOC的子对象,属于类对象,称为PhotoFetcher。它使用NSPrivateQueueConcurrencyType,因此在此MOC上执行的所有操作都在后台队列中进行 我们的应用程序从API下载表示照片数据的JSON数据。

我有一个问题,我已经研究了几个星期了。每当我保存我的核心数据管理对象上下文时,它都会影响UI性能。我已经尽我所能,并且正在寻求帮助

形势 我的应用程序使用两个
NSManagedObjectContext
实例。其中一个属于应用程序委托,并附加了一个持久存储协调器。另一个是主MOC的子对象,属于
对象,称为
PhotoFetcher
。它使用
NSPrivateQueueConcurrencyType
,因此在此MOC上执行的所有操作都在后台队列中进行

我们的应用程序从API下载表示照片数据的JSON数据。为了从API中检索数据,将执行以下步骤:

  • 构造一个
    NSURLRequest
    对象,并使用
    NSURLConnectionDataDelegate
    协议来构造从请求返回的数据,或处理错误
  • JSON数据下载完成后,在辅助MOC队列上执行一个块,执行以下操作:
  • 使用<代码> NSjSunSerialSerial//Cl>解析JSON到基础类实例。
  • 迭代已解析的数据,根据需要在后台上下文中插入或更新实体。通常,这会产生大约300个新的或更新的实体
  • 保存背景上下文。这会将我的更改传播到主主主运行中心
  • 在主主MOC上执行块以保存其上下文。这是将数据持久化到磁盘,一个
    SQLite
    存储。最后,对委托进行回调,通知他们响应已完全插入到核心数据存储中
  • 保存后台MOC的代码如下所示:

    [AppDelegate.managedObjectContext performBlock:^{
        [AppDelegate saveContext]; //A standard save: call to the main MOC
    }];
    
    Symbol Name
    -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
     -[NSManagedObjectContext executeFetchRequest:error:]
      -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]
       _perform
        _dispatch_barrier_sync_f_invoke
         _dispatch_client_callout
          __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0
           -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
            -[NSManagedObjectContext executeFetchRequest:error:]
             -[NSPersistentStoreCoordinator executeRequest:withContext:error:]
              -[NSSQLCore executeRequest:withContext:error:]
               -[NSSQLCore objectsForFetchRequest:inContext:]
                -[NSSQLCore newRowsForFetchPlan:]
                 -[NSSQLCore _newRowsForFetchPlan:selectedBy:withArgument:]
                  -[NSSQLiteConnection execute]
    
    当主对象上下文保存时,它也保存了自上次主对象上下文保存以来下载的相当数量的JPEG。目前,在iPhone4上,我们以70%的压缩率下载了15个200x200 JPEG,或者总共大约2MB的数据

    问题 这很有效,而且效果很好。我的问题是,一旦后台上下文保存,在我的视图控制器中运行的
    NSFetchedResultsController
    就会拾取传播到主MOC的更改。它在我们的
    PSTCollectionView
    中插入新的单元格,这是
    UICollectionView
    的开源克隆。在插入新单元格时,主上下文会将这些更改保存并写入磁盘。在运行iOS 5.1的iPhone4上,这可能需要250-350毫秒的时间

    在这三分之一秒内,应用程序完全没有响应。保存之前正在进行的动画将暂停,并且在保存完成之前,不会将新用户事件发送到主运行循环

    我在Instruments中运行了我们的应用程序,使用时间分析器来识别是什么阻塞了我们的主线程。不幸的是,结果相当不透明。这是我从仪器上得到的最重的堆栈跟踪

    它似乎正在将更新保存到持久存储,但我不能确定。因此,我删除了所有对
    saveContext
    的调用,这样MOC就不会触碰磁盘,并且主线程上的阻塞调用仍然存在

    文本形式的跟踪如下所示:

    [AppDelegate.managedObjectContext performBlock:^{
        [AppDelegate saveContext]; //A standard save: call to the main MOC
    }];
    
    Symbol Name
    -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
     -[NSManagedObjectContext executeFetchRequest:error:]
      -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]
       _perform
        _dispatch_barrier_sync_f_invoke
         _dispatch_client_callout
          __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke_0
           -[NSManagedObjectContext(_NestedContextSupport) _parentObjectsForFetchRequest:inContext:error:]
            -[NSManagedObjectContext executeFetchRequest:error:]
             -[NSPersistentStoreCoordinator executeRequest:withContext:error:]
              -[NSSQLCore executeRequest:withContext:error:]
               -[NSSQLCore objectsForFetchRequest:inContext:]
                -[NSSQLCore newRowsForFetchPlan:]
                 -[NSSQLCore _newRowsForFetchPlan:selectedBy:withArgument:]
                  -[NSSQLiteConnection execute]
    
    我试过的 在我们接触核心数据代码之前,我们做的第一件事就是优化我们的JPEG。我们切换到更小的JPEG格式,并看到了性能的提升。然后,我们减少了每次下载的JPEG的数量(从90个减少到15个)。这也会显著提高性能。但是,我们仍然可以在主线程上看到250-350ms长的块

    我尝试的第一件事就是去掉背景MOC,以消除它可能导致问题的可能性。事实上,它使事情变得更糟,因为我们的更新或创建代码在主线程上运行,并导致整体动画性能下降

    将持久存储更改为
    NSInMemoryStoreType
    无效


    有人能告诉我什么“秘方”可以让我获得后台管理对象上下文所承诺的UI性能吗?

    我有一些猜测,按照您应该检查它们的顺序:

  • 它看起来非常像是在数据保存后(即在回调中)发生的任何缓慢的事情。这导致我要么重新加载
    ptscolectionview
    ,要么重新获取获取的结果控制器

  • iOS 6.x上的
    UICollectionView
    是否会出现此问题?如果没有,那将导致我依赖于
    PTSCollectionView

  • 如果它仍然发生,那么这意味着它可能不是集合视图,而是获取结果控制器。从堆栈帧(尽管可能是不透明的)来看,获取的结果控制器试图通过
    调度\u屏障执行获取。这些用于确保在到达屏障之前不会执行块。我在这里有点冒险,但您可能想检查一下这是因为在内部,核心数据保存在其他地方,因此会延迟任何其他获取请求的执行。再一次,这是一种狂野的、未受过教育的猜测。但是我会在不立即更新获取的结果控制器的情况下尝试,看看你的口吃是否仍然发生


  • 另一件对我来说很突出的事情是,您在一个子
    MOC
    上执行了大量工作,但随后在父对象上执行了保存。似乎大部分保存都应该由子级执行。但也可能是因为我已经有一段时间没有处理这部分核心数据了:-)

    我会做出一些假设,但根据您的描述,我认为这是合理的

    首先,我假设您的主主运行中心是通过以下方式创建的:<