前语

最近做了一波功能优化,发现项目中有一部分控制器的 view 在没有展现给用户之前就现已加载到内存中了,这其实是一种浪费,而且不符合懒加载的原则。

那什么状况会导致控制器的 view 过早加载?应该怎么防止这种状况呢?今日就来聊聊这个论题。

UIViewController 生命周期

UIViewController 是 iOS 开发的基本组件。为了了解上边说到的提早加载控制器,咱们需求了解视图控制器的生命周期。

正常的生命周期调用顺序是:

  1. init() — 初始化视图控制器

  2. loadView() — View 被创立并加载到内存中

  3. viewDidLoad() — 加载 View 后调用

  4. viewWillAppear(_:) — 在控制器出现之前调用

  5. viewDidAppear(_:) — 在控制器出现之后调用

  6. viewWillDisappear(_:) — 在控制器消失之前调用

  7. viewDidDisappear(_:) — 在控制器消失之后调用

  8. deinit() — 在控制器毁掉之后调用

发现过早加载的问题

这儿需求要点说一下 loadView 办法和 viewDidLoad 办法。

当控制器调用 init() 创立之后,其 View 默许的值是 nil,当你第一次调用 view 的时分(即self.view) view 才会创立,这是控制器创立 view 的原理。

view 的创立是经过调用 loadView 办法进行的,loadView 调用完结之后紧接着就会调用 viewDidLoad 办法,而咱们大部分的初始逻辑是写在 viewDidLoad 里的。

此时,假如并不需求展现 view,就会导致一些额定的内存占用。

怎么防止这种问题

经过我个人项目的经验和问题排查,大概有以下几种状况会导致这种问题,需求在开发中尽量防止。

1、控制器尽量在需求的时分才加载

比方常见的多 tab 切换页面,每个 tab 都是一个控制器,这时分有一种做法是把一切 tab 的控制器都创立出来,比及某个 tab 的时分直接把创立好的拿来用。

从内存最小化的原则上来看,这种方式并不合理,控制器尽量是懒加载的,即在切到某个 tab 的时分再创立

2、防止在 init 办法中直接调用 view 特点

从控制器 init() 办法内拜访 self.view 肯定是一个过错,因为它会过早调用 loadView() 和 viewDidLoad()。一方面,不是一切调用 init 之后就会展现,另一方面这种状况往往会加剧主线程的负担,因为 viewDidLoad() 中会有比较多的逻辑。

3、创立控制器之后不要当即调用 view 特点

有些时分确实或许提早创立控制器,比方运用 UITabBarController 控件,就需求提早把 TabBar 上的控制器都创立出来,可是创立完结之后,不要在切过去之前调用控制器的 view 特点,否则还是会提早创立 view,导致功能下降。

使用大局断点发现问题

特别是大项目,这种问题很难被发现,一个一个排查又不太现实,使用大局断点能够帮助咱们快速发现这个问题。

首要选中 Xcode 左边菜单栏的断点 Tab(快捷键 command + 8),然后点击左下角加号按钮,并选择 Symbolic Breakpoint…:

如何避免过早加载控制器 view

为了能够捕获 viewDidLoad 办法的调用,需求在断点弹出框的 Symbol 中填写 -[UIViewController viewDidLoad],之后点击 Add Action 按钮添加一个日志打印,这儿咱们填写 po $arg1,最终勾选一下底部的勾选框 Automatically continue after evaluating actions,意思是当触发这个断点的时分不要停止代码运转。

如何避免过早加载控制器 view

最终,咱们当咱们再次运转代码,每个控制器在履行 viewDidLoad 办法时就会在控制器把这个控制器名称打出来,方便咱们检查哪些 viewDidLoad 被提早调用了。

如何避免过早加载控制器 view

在之前的文章中还讲过一篇关于控制器毁掉的文章,感兴趣能够去看看:

怎么检测控制器循环引用

点击下方大众号卡片,关注我,每天共享一个关于 iOS 的新知识

本文同步自微信大众号 “iOS新知”,每天准时共享一个新知识,这儿只是同步,想要及时学到就来关注我吧!