【问题标题】:iOS How to determine what is blocking the UIiOS 如何确定是什么阻塞了 UI
【发布时间】:2013-04-02 10:09:46
【问题描述】:

我对 iOS 开发还很陌生,但我开始掌握一些更复杂的概念。我目前有一个实现 AVCam 来捕获视频的应用程序。 AVCam 在单独的线程上创建,但使用我的主 xib 文件中的视图。当相机完成捕捉后,它会在我的 ViewController 类中调用一个完整的函数。在完整的函数中,我调用了许多更新 UI 的其他函数以及一些 NSLog。一切似乎都正常,我立即在控制台中看到了日志,但 UI 还需要 3 秒才能更新。我尝试使用工具来查找有问题的代码,但我似乎找不到它。还有其他方法可以确定 UI 阻止了什么?

这是录制完成时调用的代码;

-(void)movieRecordingCompleted{
       [HUD hide:YES];
        NSLog(@"movieRecordingCompleted");
        [self showModalViewController];
        NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.mov"];
        NSLog(@"pathToMovie: %@", pathToMovie);
        pathToTreatedVid = pathToMovie;
        NSLog(@"File Save Called");
        UISaveVideoAtPathToSavedPhotosAlbum(pathToMovie, nil, NULL, NULL);
}

一切都立即记录下来,但是进度HUD和模态视图控制器大约2-5秒都没有触发,这很奇怪。

这是线程的前后状态(冻结时与解冻时)。

【问题讨论】:

  • 请显示一些更新 UI 的代码。

标签: ios multithreading user-interface blocking


【解决方案1】:

我喜欢使用这个功能:

sleep(x);

其中“x”是秒数......它只会阻塞它在该秒数上运行的任何线程。如果您的 UI 冻结,您就知道您阻塞了主线程,因此该代码正在主线程上运行(无论您是否有意)。尝试将此调用放在代码中的不同位置,它应该可以帮助您诊断正在发生的事情。希望对您有所帮助。

【讨论】:

  • 您可以通过在其中放置一个断点来准确地找出一个方法正在哪个线程上运行。当遇到断点时,Xcode 会在左侧面板中突出显示线程。
  • 是的,5 年后人们还在反对这个?这是一个年轻的轻率!如果可以的话,我会删除它:)
【解决方案2】:

您可以使用Time Profiler 找出阻止您应用的原因。

【讨论】:

  • 我尝试使用时间分析器,当我这样做时,我看到我的进程启动了一个新线程,并且该威胁的运行时间增加了。当该过程“完成”时,即当我看到我的完整函数被调用时,运行时间停止增加......但主函数没有开始增加。就像应用程序由于某种原因被临时冻结
【解决方案3】:

尝试暂停程序执行(在Xcode的底部面板中有一个按钮,第三个)

  • 然后看左侧面板(Navigator panel),
  • 查找Debug Navigator

  • 找到带有main函数的线程,你可以通过这个线程中的方法找出更新你的UI需要这么长时间。目前有效的方法通常是顶部黑色的(灰色列出obj-c 内部方法)。

【讨论】:

  • 这种方法可能需要多次尝试才能产生有用的结果,因此请确保您有耐心。
  • 我刚刚尝试过这种方法,当 UI 被“冻结”时,我看到了许多线程(当我按队列排序时)。主要的(com.apple.main)一个叫做 RemoteClientNotifyQueue 一个叫做 com.apple.libdispatch-manager 但我不太确定这些信息对我有什么帮助
  • @Ossir 我在暂时冻结的状态下暂停了应用程序,然后当它变得响应后,它接缝的区别是3个线程,他们到底在做什么对我来说仍然是个谜。 (见附图)
  • UI 仅从主线程更新。主线程是一个包含main 方法的线程。您应该定期检查此线程以查看在您的应用程序冻结的时间段内执行了哪些方法。其他线程被认为是后台线程,不影响UI更新(实际上它们可以调用UI更新函数,但是UI无论如何都会在主线程中刷新)
  • 另外,不要忘记UISaveVideoAtPathToSavedPhotosAlbum()函数是异步执行的,所以它需要一些时间才能完成。
【解决方案4】:

您可以通过在配置文件模式下运行您的应用程序来使用 Instruments 中的系统跟踪工具。然后,您将获得系统中所有线程的详细运行情况,以及线程经过的每个调度事件的堆栈跟踪。

2016 年 WWDC System Trace in Depth 有一个很棒的视频,它将引导您调试阻塞的线程问题。

这比 Time Profiler 工具要好得多,因为该工具的工作原理是每隔一段时间对 CPU 上运行的内容进行采样。但是,如果您的线程被阻塞,它就不会在 CPU 上运行,所以 - 它不会被采样。您的主线程可能被阻塞了一秒钟,但它不会显示在 Time Profiler 中。

【讨论】:

  • 为此 +1 — 不要以为我以前从没看过 WWDC 视频,但这真的让我大开眼界
  • 视频好像被删除了?
  • 至少目前,这里有一份副本:wwdctogether.com/wwdc2016/411
【解决方案5】:

Michael Cueno 的建议非常棒 - 描述了系统跟踪分析和“兴趣点”。

在 iOS 12+ 中还有另一种工具可以使用路标来执行此操作:https://pspdfkit.com/blog/2018/using-signposts-for-performance-tuning-on-ios/

这个想法是你使用来自os框架的函数os_signpost,并将它插入你怀疑阻塞主线程的所有地方。

说,你按下 UIButton 有时它会滞后。 您应该在 buttonPressed 处理程序和您怀疑的所有函数中插入 os_signpost

然后,您应该在 Instruments 中配置文件,使用 Blank 模板并添加 os_signpost 子模板。一个例子在这里:https://github.com/gatamar/UnsplashSearch

注意 Swift / Objc 中的 API 差异:

斯威夫特:

import os

...

static let pointsOfInterest = OSLog(subsystem: "com.apple.SolarSystem", category: .pointsOfInterest)
os_signpost(.begin, log: ViewController.pointsOfInterest, name: "createSubviews")
defer {
    os_signpost(.end, log: ViewController.pointsOfInterest, name: "createSubviews")
}

对象:

#include <os/log.h>
#include <os/signpost.h>

os_log_t log = os_log_create("com.apple.SolarSystem", "YourCategory");
os_signpost_id_t spid = os_signpost_id_generate(log);

...

os_signpost_interval_begin(log, spid, "func1");

... 

os_signpost_interval_end(log, spid, "func1);

【讨论】:

    【解决方案6】:

    如果您在模拟器上遇到UI 阻塞问题

    在深入研究任何其他方法之前,请尝试退出模拟器并删除应用程序并再次重建/运行应用程序,在我的情况下,我找不到任何线程问题或崩溃,经过近一个小时的仪器挖掘后,我只是认为这可能是模拟器问题,就是这样:)

    【讨论】:

      猜你喜欢
      • 2011-04-15
      • 2013-09-08
      • 1970-01-01
      • 2012-04-15
      • 2021-09-29
      • 2016-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多