presentViewController 的性能慢 - 取决于被呈现控制器的复杂程度?

20

我正在呈现一个视图控制器:

SCAAboutController2 *controller = [[SCAAboutController2 alloc] initWithNibName:nil bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navController animated:YES completion:nil];

设备在呈现之前会停顿3-4秒。我尝试使用Instruments诊断,但似乎大部分时间都花费在main上。

enter image description here

这是相同的配置文件,但隐藏了系统库:

enter image description here

对我来说,这些消息都不可识别,所以我不知道如何开始调试性能问题。

我在其他地方读到应该检查主代码是否在主线程上执行。然而,以下更改并没有改善任何事情:

dispatch_async(dispatch_get_main_queue(), ^{
        SCAAboutController2 *controller = [[SCAAboutController2 alloc] initWithNibName:nil bundle:nil];
        UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
        [self presentViewController:navController animated:YES completion:nil];
    });

我已经很快地没有了如何进展的想法。我应该如何进一步调查,或者缓慢呈现的根本原因是什么?

编辑

一些令人困惑的发现:

  • 我已经从呈现的视图控制器中删除了所有代码。性能没有受到影响。
  • 我还有另一个控制器,通过不同的按钮从同一位置呈现。它同样很慢。
  • 呈现ing控制器有很多子视图和约束,甚至有一些子视图控制器。删除填充这些内容的代码可以解决问题。
  • 在呈现控制器的viewWillDisappear中没有添加任何内容。

编辑2

我发现问题集中在我在主(呈现)控制器中添加的一系列布局约束上。具体而言,我遍历一些子控制器(类型为teamController),并添加约束:

[self.browser addConstraint:[NSLayoutConstraint constraintWithItem:teamController.view
                                                         attribute:NSLayoutAttributeWidth
                                                         relatedBy:NSLayoutRelationEqual
                                                            toItem:self.browser
                                                         attribute:NSLayoutAttributeWidth
                                                        multiplier:1
                                                          constant:0]];

只有10个子控制器。同样奇怪的是,如果我使用以下代码就没有这个问题:

[self.browser.contentView addConstraint:[NSLayoutConstraint constraintWithItem:teamController.view
                                                                     attribute:NSLayoutAttributeWidth
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:nil
                                                                     attribute:NSLayoutAttributeNotAnAttribute
                                                                    multiplier:1
                                                                      constant:200]];
我仍然对为什么这些限制会导致展示另一个模态框停滞感到困惑,并且为什么其中一种限制的变体表现出截然不同的行为感到困惑。

UI 总是在主线程中执行。 - Kunal Balani
1
NSIS*与Autolayout引擎相关。在呈现的视图控制器中会发生什么?视图如何布局?有多少个子视图? - jrturton
谢谢你的提示 - 请看我的编辑。我有一些布局代码可以优化,但它在当前的控制器中 :/ - Ben Packard
@BenPackard,你有找到任何解决方案吗?如果是,请在此问题下发布一个答案。 - G.Abhisek
没有可行的解决方案。 - Ben Packard
5个回答

63

我不确定这是否是原作者的问题,但这里有一个解决类似问题的方法:我正在尝试在 didSelectRowAtIndexPath 中展示一个视图,并且在此之前必须调用 deselectRowAtIndexPath。如果这能帮助到某个人......


9
对我而言,只需在self.presentViewController之前添加tableView.deselectRowAtIndexPath(indexPath, animated: false)就可以解决我的问题。有人能解释一下这是为什么吗? - Paul
1
这对我也解决了问题,我很想知道更多的解释。 - cybrox
2
这解决了我的问题,但是只有上帝知道它的原理。有人能解释一下吗? - trapper
2
我认为这是一个可能的解释。https://dev59.com/umEi5IYBdhLWcg3wptp_ - Eugene H
由于这个答案变得有点受欢迎,让我提一下调用[super didSelectRowAtIndexPath:indexPath]可能是实现相同结果的更清晰的方法。 - vib
显示剩余6条评论

7
您的应用程序因对集合进行多次添加和删除操作而无响应。如果在主线程上执行任何重型处理,应用程序会被阻塞并变得无响应。
简单地说,您有一个循环,在主线程上向集合中添加对象,这使得您的应用程序变慢。
如果查看仪器,可以看到视图加载按钮和渲染非常快。initWithFrame、viewDidLoad的时间少于总时间的4%。大部分时间都是由NSISVairable释放和保留所占用的,这大约发生了500次。因此,您正在对集合对象进行某些操作,可能是在循环中。保留和释放需要时间,不应在主线程上完成。
简单的解决方案是:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 在后台线程中处理所有繁重的工作。 });
为什么以下代码没有改善任何内容:
dispatch_async(dispatch_get_main_queue(), ^{ SCAAboutController2 *controller = [[SCAAboutController2 alloc] initWithNibName:nil bundle:nil]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller]; [self presentViewController:navController animated:YES completion:nil]; });
因为您已经在主线程上执行此操作,而且您需要在后台线程上调用main_queue。您不应在后台线程上执行任何UI操作。

谢谢,我会查看是否有任何地方我可能正在操作 NSSets。请还要注意我的编辑 - 我已经从所呈现的控制器中删除了所有代码,但性能并没有改善。 - Ben Packard
@BenPackard 如果您添加一些操作数据或包含循环的SCAAboutController2代码,那将非常有帮助。 - Kunal Balani
SCAAboutController2是空的。 - Ben Packard

5
我曾经遇到过类似的问题,即回调函数在不同的线程上而不是主线程上。使用以下方法可以解决这个问题:
            DispatchQueue.main.async {
                 Your_UI_update_function()
            }

该解决方案解决了UI更新缓慢的问题。


4
在以下代码中添加一行:

CFRunLoopWakeUp(CFRunLoopGetCurrent());

在以下代码之后:

[self presentViewController:navController animated:YES completion:nil];

对于我来说,这个修复了问题。
答案摘自此线程。感谢Eugene H的评论。

0

我知道我回答晚了一年。但如果对其他人有帮助的话。

尝试放置(Swift代码)

self.view.layer.shouldRasterize = true;
self.view.layer.rasterizationScale = UIScreen.mainScreen().scale;

对于正在被呈现的视图控制器(如果在那里不起作用,请尝试将其放在父级上。我已经将其放在了两个位置,这有助于使所有其他动画更加平滑)

希望这可以帮到你。


将此代码添加到'viewWillAppear'还是'viewDidLoad'中? - DeyaEldeen
尝试在viewDidAppear中添加它。 - Kunal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接