【问题标题】:Process Core Data in background thread after a delay延迟后在后台线程中处理核心数据
【发布时间】:2015-07-28 07:29:25
【问题描述】:

我有一个表格视图来显示从 API 获取的列表。

首先,我将 API 数据存储到 Core Data,然后在后续应用启动时,我将尝试更新列表。

我需要在后台线程中处理更新操作。应该在启动特定 ViewController 5 秒后调用更新操作

func updateGlossary() {
    var GlobalMainQueue: dispatch_queue_t {
        return dispatch_get_main_queue()
    }
    let delayInSeconds = 5.0
    let popTime = dispatch_time(DISPATCH_TIME_NOW,
        Int64(delayInSeconds * Double(NSEC_PER_SEC)))
    dispatch_after(popTime, GlobalMainQueue) {
        self.showAlertMessage(message: "Updating Glossary")
        DataStore.GetToken({ (token, error) in
            //Got Token
           //Call the API for updated data and store it to core data
        })
    }
}

这里面有两个问题;

  1. 进行更新时 UI 被阻止
  2. 当我在更新操作开始之前从控制器返回时,它仍然会从其他控制器更新(我认为问题是因为排队)

我正在使用 SwiftAlamofireCore Data

【问题讨论】:

    标签: ios multithreading swift core-data alamofire


    【解决方案1】:

    第一个问题:-

    更新进行时用户界面被阻止

    你应该以异步方式调用updateGlossary操作,它不会阻塞主线程:-

    dispatch_async( dispatch_get_global_queue(0, 0), ^{
        [self performSelector:@"updateGlossary" withObject:nil afterDelay:5.0]
    });
    

    第二个问题:-

    当我在更新操作开始之前从控制器返回时,它仍然会从其他控制器更新(我认为问题是因为排队)

    只需在视图控制器的 viewDidDisapper() 方法中停止线程

    -(void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:FALSE];
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(updateGlossary) object:nil];
    }
    

    【讨论】:

      【解决方案2】:

      不,不要使用dispatch_async 进行 CoreData 操作。 NSManagedObjectContext 本身不是线程安全的,会导致意外行为。你可能仍然使用dispatch_after 来调用主线程上的块。

      您的代码很好,只是您的 getToken(:_) 可能发生在主线程上和/或您的数据库操作发生在主线程上,因此阻塞了 UI。

      你可以像这样修改你的代码:

      func updateGlossary() {
          var GlobalMainQueue: dispatch_queue_t {
              return dispatch_get_main_queue()
          }
          let delayInSeconds = 5.0
          let popTime = dispatch_time(DISPATCH_TIME_NOW,
              Int64(delayInSeconds * Double(NSEC_PER_SEC)))
          dispatch_after(popTime, GlobalMainQueue) {
              self.showAlertMessage(message: "Updating Glossary")
              DataStore.GetToken({ (token, error) in
                  //Got Token
                 //This will execute in the background
                 self.callAPIWithToken(token, success: { (data) -> () in
                    //Callback on the main thread
                    self.privateManagedObjectContext.performBlock {
                      //Will execute in its private thread
                      //Insert to the database, save context
                    }
                 }) { (error) -> () in
      
                 }
              })
          }
      }
      

      privateManagedObjectContextNSManagedObjectContext 的一个实例,其并发类型为.PrivateQueueConcurrencyType,其父存储为.MainQueueConcurrencyType。您使用privateManagedObjectContext 在后台插入并保存您的数据。这种方法是线程安全的,不会阻塞您的 UI。

      Here 是一篇关于多上下文核心数据系统的好文章。

      编辑 如果您想在离开时取消,我不建议使用dispatch_after。请改用NSTimer。让它在 5 秒后在主线程上进行服务调用;当您离开控制器时使其无效。

      【讨论】:

      • var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as!AppDelegate) var context: NSManagedObjectContext = appDel.managedObjectContext!这是我的上下文,当我使用它时,出现错误 NSInvalidArgumentException',原因:'Can only use -performBlock: on an NSManagedObjectContext that was created with a queue.'
      • 正确。这是因为您使用的上下文是.ConfinementQueueConcurrencyType 类型。请通读我与文章链接的文章。这个方法对你来说会更清楚。
      • 效果很好,但是如何在离开控制器时取消排队的任务
      • 是取消请求还是取消数据库插入?
      【解决方案3】:

      UI 被阻塞,因为您在 mainQueue 上进行 API 调用。创建一个不同的队列并进行 API 调用,稍后在您到达主队列后将 API 数据插入 Coredata。

      我可以从您的代码中看到您正在使用GlobalMainQueue,它是dispatch_get_main_queue 的别名,因此您的 UI 卡住了。

      解决方法如下:

       var GlobalBackgroundQueue: dispatch_queue_t
      {
        return dispatch_get_global_queue(Int(QOS_CLASS_BACKGROUND.value), 0)
      }
      
      dispatch_after(popTime, GlobalBackgroundQueue) 
      { 
          //Make the API Call
          dispatch_async(dispatch_get_main_queue()) 
          {
              //Insert Into CoreData
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-12
        • 1970-01-01
        • 2011-09-28
        • 1970-01-01
        • 2019-09-29
        • 2012-11-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多