Swift - 以编程方式创建视图时的 Lazy Var vs. Let(节省内存)

Posted

技术标签:

【中文标题】Swift - 以编程方式创建视图时的 Lazy Var vs. Let(节省内存)【英文标题】:Swift - Lazy Var vs. Let when creating views programmatically (saving memory) 【发布时间】:2018-05-02 05:20:23 【问题描述】:

我是一个初学者,我有点理解 Lazy Var vs. Let。我注意到它在使用 Lazy Var 时节省了大量的内存使用,尤其是与 ImageViews 一起使用。但是到目前为止我看到的教程和指南并不经常使用 Lazy Var,所以我怀疑这是一种不好的做法,而且我忽略了一些东西。

我做了一些研究,了解到 Lazy 不是“线程安全的”,但我不明白这是什么意思。我已经看到了很多优点和缺点,但我无法得出任何结论,尤其是因为我的知识非常有限。

在创建 UIView 时,什么时候可以(或更好)使用 Lazy Var 与 Let?

lazy var profileImageView: UIImageView = 

    let imageView = UIImageView(image: #imageLiteral(resourceName: "page1"))
    imageView.translatesAutoresizingMaskIntoConstraints = false
    imageView.contentMode = .scaleAspectFit
    return imageView

()

【问题讨论】:

这是对lazy var 是什么的较早(看起来是 Swift 1)的解释。具体查看何时使用它的部分:mikebuss.com/2014/06/22/lazy-initialization-swift 现在,查看您的问题右侧栏中的三个相关链接 suggest 它们与您的相似,我发现没有 真的回答问题 - 就像上面的链接 - 关于*何时使用每个。他们要么解释 lazy 是什么(以及为什么你必须使用 var 或为什么某些东西不能构建。我对此表示赞成,希望这里的人可以给你一个好问题的好答案 【参考方案1】:

您是否会使用lazy var 取决于您的代码及其上下文。它本身并不坏或好。你必须决定什么时候合适。

在您做出决定之前,您必须知道lazy var 是什么。

lazy var 是什么?

延迟初始化是一个概念,其中变量内容的初始化(构造)被延迟到第一次使用。首次访问此类变量会触发初始化。由于在使用(需要)变量之前不会创建内容,因此使用惰性初始化变量可以节省资源。

这是延迟初始化背后的主要驱动力。除非你需要它,否则你不会创造东西。这也是您在决定是否应为 lazy var 时使用的逻辑。

如果您正在处理始终可见(需要)的视图(或其他任何内容),则使用延迟初始化毫无意义。另一方面,当您处理并不总是需要的实例时 - 使用 lazy var 是合理的。

如果您的视图在呈现的视图控制器中始终可见,那么通过使其变得懒惰您将不会完成很多工作。如果它仅在特定情况下可见 - 例如当用户展开一些折叠的面板时 - 那么让它变得懒惰是有意义的。默认情况下,它将使您的视图控制器加载更快并使用更少的内存。


就线程安全而言,lazy var 在 Swift 中不是线程安全的。

这意味着如果两个不同的线程试图同时访问同一个lazy var,在这个变量被初始化之前,其中一个线程可能会访问部分构造的实例。

您可以在以下位置找到更多关于线程安全的信息:

Swift - is lazy var thread-safe?

Make "lazy var" threadsafe

【讨论】:

【参考方案2】:

使用lazy var 的另一个好处是提高代码的可读性。

在您的示例中,与图像视图相关的代码被组合在一起,而不是分散到初始化程序、设置函数或viewDidLoad。这通过不需要代码的读者冒险到代码中的各个地方来了解您的视图是如何配置的,从而改进了本地推理。要了解你的观点,他们只需要跳转到它的声明。

标记为lazy var 的初始化闭包可以访问self,允许在闭包内完成更多配置,例如添加目标操作或引用其他常量属性。

我会考虑使用 lazy var 的闭包来初始化属性(尤其是视图)是一种很好的做法,而且它似乎也在 Swift 社区中越来越受欢迎。

根据项目的不同,节省开发人员时间可能比节省系统内存更有价值。

【讨论】:

【参考方案3】:

惰性变量的使用可以为一个矛盾的问题提供一种解决方法:您希望创建一个自定义视图,该视图具有其初始化引用父视图的子视图。

例如,如果要创建包含相同大小的子 UIScrollView 的 UIView 的子类,则不能声明包含:

var m_scrollView: UIScrollView

override init(frame: CGRect)

    m_scrollView = UIScrollView(frame: self.frame)
    super.init(frame: frame)

编译器会在调用 super.init 之前抱怨你引用了 self。但是... super.init 必须在所有成员初始化之后调用

解决这个循环问题的方法是让 m_scrollView 变得懒惰并在它的声明中初始化它:

lazy var m_scrollView = UIScrollView(frame: self.frame)

【讨论】:

有时我倾向于在这些情况下使用惰性。但是,我不确定这是否会在以后导致一些奇怪的错误。我宁愿在这里为UIScrollView添加一个设置方法,使用约束而不是使用惰性初始化。

以上是关于Swift - 以编程方式创建视图时的 Lazy Var vs. Let(节省内存)的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 以编程方式创建视图问题,无法找到存储的属性

Swift - 以编程方式向堆栈视图添加标签

以编程方式创建 UILabel、UITextField 等时的图形错误

Swift 3:不能以编程方式在 Collectionview 标题上正确创建标签?

在 swift 中以编程方式在堆栈视图中添加标签和文本视图

根据 contenView 大小以编程方式调整滚动视图的大小 [Swift]