【问题标题】:iOS - Process data without freezing the UIiOS - 在不冻结 UI 的情况下处理数据
【发布时间】:2012-08-07 20:22:48
【问题描述】:

我需要完成以下任务:

1) 从 sqlite 数据库中读取一些数据

2) 处理数据

3) 用处理后的数据生成一些图表

如果我有一个用户在应用程序中输入了许多数据,那么有一天这个分析系统可能会变慢并冻结 UI。

那么,处理它的正确方法是什么,允许用户与 UI 交互,并选择取消操作或退出屏幕?

我需要为我的所有任务创建简单的线程并使用取消事件或标志来停止每个任务?还是有别的办法?

例如:

任务 1:在线程中从 sqlite 读取数据,并在需要时使用标志停止进程。

任务 2:在线程中处理数据,并在需要时使用标志停止进程。

任务 3:将数据交付给第 3 方组件。此时,是否可以取消正在其他组件上运行的操作?

我的想法是否正确,或者我可以改进一些东西?

【问题讨论】:

  • 阅读苹果文档关于构建响应式 UI developer.apple.com
  • 用词NSOperation 替换词Thread,你应该有你需要的所有后台线程和取消选项:)
  • 我会看看 NSOperation,谢谢 :)
  • 不要使用 NSOperation,使用 GCD 或 Grand Central Dispatch。这使得代码简单易懂,Apple 推荐使用。
  • @HannesSverrisson 看看我对你的回答的评论 - 我不认为它像你说的那样被切割和干燥:(

标签: objective-c ios multithreading user-interface freeze


【解决方案1】:

这是 Apple 推荐的最快的 GCD(Grand Central Dispatch)方式。它也更容易阅读和理解,因为逻辑是线性的,但没有在方法之间分割。

请注意,它显示了weakSelf“舞蹈”,如果异步可能比它所调用的控制器寿命更长,则它是必要的,因此它对其进行弱引用,然后检查它是否处于活动状态并保留它以进行更新:

Swift 4 & 3

 DispatchQueue.global().async() {
      print("Work Dispatched")
      // Do heavy or time consuming work

      // Then return the work on the main thread and update the UI
      // Create weak reference to self so that the block will not prevent it to be deallocated before the block is called.
      DispatchQueue.main.async() {
           [weak self] in
           // Return data and update on the main thread, all UI calls should be on the main thread 
           // Create strong reference to the weakSelf inside the block so that it´s not released while the block is running
           guard let strongSelf = self else {return}
           strongSelf.method()
      }
 }

Objective-C

// To prevent retain cycles call back by weak reference
   __weak __typeof(self) weakSelf = self;  // New C99 uses __typeof(..)

    // Heavy work dispatched to a separate thread
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"Work Dispatched");
        // Do heavy or time consuming work
        // Task 1: Read the data from sqlite
        // Task 2: Process the data with a flag to stop the process if needed (only if this takes very long and may be cancelled often).

        // Create strong reference to the weakSelf inside the block so that it´s not released while the block is running
        __typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {

            [strongSelf method];

            // When finished call back on the main thread:
            dispatch_async(dispatch_get_main_queue(), ^{
                // Return data and update on the main thread
                // Task 3: Deliver the data to a 3rd party component (always do this on the main thread, especially UI).
            });
        }
    });

取消进程的方法是包含一个 BOOL 值并将其设置为在不再需要正在完成的工作时从主线程停止。但是,这可能不值得,因为除非进行大量计算,否则用户不会注意到后台工作太多。 为了防止保留循环,请使用弱变量,例如:

__weak __typeof(self) weakSelf = self; // Obj-C
[weak self] in

并在块内使用强引用调用weakSelf(以防止调用VC已被释放时崩溃)。您可以使用 UIViewController 之类的确切类型或 __typeof() 函数(在 C99 中您需要使用 __typeof(..) 但以前您可以直接使用 __typeof(..))来引用实际类型:

Objective-C

__typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
   [strongSelf method];
}

Swift 4 & 3

if let weakSelf = self {
   weakSelf.method()
}

// If not referring to self in method calls.
self?.method()

注意:使用 GCD 或 Grand Central Dispatch,这是最简单的,Apple 推荐的方式,代码流按逻辑顺序排列。

【讨论】:

  • Hannes,用 GCD 代替 NSOperation 更好吗?两者都让你做同样的事情还是有很大的不同?
  • 代码更简单,使用 GCD 集中在一处。它是 iOS 的新功能,是 Apple 推荐的方式,而且效率也很高。它使用了块,但正如您从上面看到的那样,代码很简单,在一个地方,并且在正确的逻辑工作顺序中。
  • 尽管(扮演魔鬼的倡导者)NSOperation 为您提供优先级、池中的多个线程和免费的 isCancelled 功能,但代价是让您将代码放在不同的类中。如果您正在做一个小操作,请使用 GCD。如果涉及更多,我会使用 NSOperation。 (注意,我不知道这两种方法的分界线在哪里:)
  • GCD 有多个线程,可能有不同的优先级和异步和同步,即上面的开发人员也可以使用此自定义创建自己的线程,但此任务不需要它。 Apple 强调使用 GCD。
  • Psst...在向对象发送消息之前,您无需进行 nil 检查。
【解决方案2】:

这是从主线程中分离函数的方法 <pre>[NSThread detachNewThreadSelector:@selector(yourmethode:) toTarget:self withObject:nil];</pre>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    • 2021-12-07
    • 2011-12-27
    • 2012-04-21
    • 2016-09-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多