在 Swift 中使用闭包实现懒加载

在 Swift 中使用闭包实现懒加载

学习如何兼顾模块化与可读性来建立对象

图为苹果的 Magic Keyboard 2 与 Magic Mouse 2

亲爱的读者大家好!我是 Bob,很高兴能在这篇文章中与大家相遇!如你想加入个人邮件列表,获取更多学习 iOS 开发的文章,请点击这儿注册,很快就能完成的哦 :)javascript

动机

在我刚开始学习 iOS 开发的时候,我在 YouTube 上找了一些教程。我发现这些教程有时候会用下面这种方式来建立 UI 对象:前端

let makeBox: UIView = {
 let view = UIView()
 return view
}()复制代码

做为一个初学者,我天然而然地复制并使用了这个例子。直到有一天,个人一个读者问我:“为何你要加上{}呢?最后为何要加上一对()呢?这是一个计算属性吗?”我哑口无言,由于我本身也不知道答案。java

所以,我为过去年轻的本身写下了这份教程。说不定还能帮上其余人的忙。react

目标

这篇教程有一下三个目标:第一,了解如何像前面的代码同样,很是规地建立对象;第二,知道编在写 Swfit 代码时,何时该使用 lazy var;第三,快加入个人邮件列表呀。android

预备知识

为了让你能轻松愉快地和我一块儿完成这篇教程,我强烈推荐你先了解下面这几个概念。ios

  1. 闭包
  2. 捕获列表与循环引用 [weak self]
  3. 面向对象程序设计

建立 UI 组件

在我介绍“很是规”方法以前,让咱们先复习一下“常规”方法。在 Swift 中,若是你要建立一个按钮,你应该会这么作:git

// 设定尺寸
let buttonSize = CGRect(x: 0, y: 0, width: 100, height: 100)

// 建立控件
let bobButton = UIButton(frame: buttonSize)
bobButton.backgroundColor = .black
bobButton.titleLabel?.text = "Bob"
bobButton.titleLabel?.textColor = .white复制代码

这样作没问题github

假设如今你要建立另外三个按钮,你极可能会把上面的代码复制,而后把变量名从 bobButton 改为 bobbyButton数据库

这未免也太枯燥了吧。编程

// New Button 
let bobbyButton = UIButton(frame: buttonSize)
bobbyButton.backgroundColor = .black
bobbyButton.titleLabel?.text = "Bob"
bobbyButton.titleLabel?.textColor = .white复制代码

为了方便,你能够:

使用快捷键:ctrl-cmd-e 来完成这个工做。

若是你不想作重复的工做,你也能够建立一个函数。

func createButton(enterTitle: String) -> UIButton {
 let button = UIButton(frame: buttonSize)
 button.backgroundColor = .black
 button.titleLabel?.text = enterTitle
 return button
}
createButton(enterTitle: "Yoyo") // 👍复制代码

然而,在 iOS 开发中,不多会看到一堆如出一辙的按钮。所以,这个函数须要接受更多的参数,如背景颜色、文字、圆角尺寸、阴影等等。你的函数最后可能会变成这样:

func createButton(title: String, borderWidth: Double, backgrounColor, ...) -> Button复制代码

可是,即便你为这个函数加上了默认参数,上面的代码依然不理想。这样的设计下降了代码的可读性。所以,比起这个方法,咱们仍是采用上面那个”单调“的方法为妙。

到底有没有办法让咱们既不那么枯燥,还能让代码更有条理呢?固然咯。咱们如今只是复习你过去的作法——是时候更上一层楼,展望你将来的作法了。

介绍”很是规“方法

在咱们使用”很是规“方法建立 UI 组件以前,让咱们先回答一下最开始那个读者的问题。{}是什么意思,它是一个计算属性吗?

固然不是,它只是一个闭包

首先,让我来示范一下如何用闭包来建立一个对象。咱们设计一个名为Human的结构:

struct Human {
 init() {
  print("Born 1996")
 }
}复制代码

如今,让你看看怎么用闭包建立对象:

let createBob = { () -> Human in
 let human = Human()
 return human
}

let babyBob = createBob() // "Born 1996"复制代码

若是你不熟悉这段语法,请先中止阅读这篇文章,去看看 Fear No Closure with Bob 充充电吧。

解释一下,createBob 是一个类型为 ()-> Human 的闭包。你已经经过调用 createBob() 建立好了一个 babyBob 实例。

然而,这样作你建立了两个常量:createBobbabyBob。如何把全部的东西都放在一个声明中呢?请看:

let bobby = { () -> Human in
 let human = Human()
 return human
}()复制代码

如今,这个闭包经过在最后加上 () 执行了本身,bobby 如今被赋值为一个 Human 对象。干的漂亮!

如今你已经学会了使用闭包来建立一个对象

让咱们应用这个方法,模仿上面的例子来建立一个 UI 对象吧。

let bobView = { () -> UIView in
 let view = UIView()
 view.backgroundColor = .black
 return view
}()复制代码

很好,咱们还能让它更简洁。实际上,咱们不须要为闭包指定类型,咱们只须要指定 bobView 实例的类型就够了。例如:

let bobbyView: **UIView** = {
 let view = UIView()
 view.backgroundColor = .black
 return view
}()复制代码

Swift 可以经过关键字 return 推导出这个闭包的类型是 () -> UIView

如今看看,上面的例子已经和我以前惧怕的“很是规方式”同样了。

使用闭包建立的好处

咱们已经讨论了直接建立对象的单调和使用构造函数带来的问题。如今你可能会想“为何我非得用闭包来建立?”

重复起来更容易

我不喜欢用 Storyboard,我比较喜欢复制粘贴用代码来建立 UI 对象。实际上,在个人电脑里有一个“代码库”。假设库里有个按钮,代码以下:

let myButton: UIButton = {
 let button = UIButton(frame: buttonSize)
 button.backgroundColor = .black
 button.titleLabel?.text = "Button"
 button.titleLabel?.textColor = .white
 button.layer.cornerRadius = 
 button.layer.masksToBounds = true
return button
}()复制代码

我只须要把它整个复制,而后把名字从 myButton 改为 newButtom 就好了。在我用闭包以前,我得重复地把 myButton 改为 newButtom ,甚至要改上七八遍。咱们虽然能够用 Xcode 的快捷键,但为啥不使用闭包,让这件事更简单呢?

看起来更简洁

因为对象对象会本身编好组,在我看来它更加的简洁。让咱们对比一下:

// 使用闭包建立 
let leftCornerButton: UIButton = {
 let button = UIButton(frame: buttonSize)
 button.backgroundColor = .black
 button.titleLabel?.text = "Button"
 button.titleLabel?.textColor = .white
 button.layer.cornerRadius = 
 button.layer.masksToBounds = true
return button
}()

let rightCornerButton: UIButton = {
 let button = UIButton(frame: buttonSize)
 button.backgroundColor = .black
 button.titleLabel?.text = "Button"
 button.titleLabel?.textColor = .white
 button.layer.cornerRadius = 
 button.layer.masksToBounds = true
return button
}()复制代码

vs

// 手动建立
let leftCornerButton = UIButton(frame: buttonSize)
leftCornerButton.backgroundColor = .black
leftCornerButton.titleLabel?.text = "Button"
leftCornerButton.titleLabel?.textColor = .white
leftCornerButton.layer.cornerRadius = 
leftCornerButton.layer.masksToBounds = true

let rightCornerButton = UIButton(frame: buttonSize)
rightCornerButton.backgroundColor = .black
rightCornerButton.titleLabel?.text = "Button"
rightCornerButton.titleLabel?.textColor = .white
rightCornerButton.layer.cornerRadius = 
rightCornerButton.layer.masksToBounds = true复制代码

尽管使用闭包建立对象要多出几行,可是比起要在 rightCornerButton 或者 leftCornerButton 后面狂加属性,我仍是更喜欢在 button 后面加属性。

实际上若是按钮的命名特别详细时,用闭包建立对象还能够少几行。

恭喜你,你已经完成了咱们的第一个目标

懒加载的应用

辛苦了!如今让咱们来看看这个教程的第二个目标吧。

你可能看过与下面相似的代码:

class IntenseMathProblem {
 lazy var complexNumber: Int = {
  // 请想象这儿要耗费不少CPU资源
  1 * 1
 }()
}复制代码

lazy 的做用是,让 complexNumber 属性只有在你试图访问它的时候才会被计算。例如:

let problem = IntenseMathProblem 
problem()  // 此时complexNumber没有值复制代码

没错,如今 complexNumber 没有值。然而,一旦你访问这个属性:

problem().complexNumber // 如今回返回1复制代码

lazy var 常常用于数据库排序或者从后端取数据,由于你并不想在建立对象的时候就把全部东西都计算、排序。

实际上,因为对象太大了致使 RAM 撑不住,你的手机就会崩溃。

应用

如下是 lazy var 的应用:

排序

class SortManager {
 lazy var sortNumberFromDatabase: [Int] = {
  // 排序逻辑
  return [1, 2, 3, 4]
 }()
}复制代码

图片压缩

class CompressionManager {
 lazy var compressedImage: UIImage = {
  let image = UIImage()
  // 压缩图片的
  // 逻辑
  return image
 }()
}复制代码

Lazy的一些规定

  1. 你不能把 lazylet 一块儿用,由于用 lazy 时没有初值,只有当被访问时才会得到值。
  2. 你不能把它和 计算属性 一块儿用,由于在你修改任何与 lazy 的计算属性有关的变量时,计算属性都会被从新计算(耗费 CPU 资源)。
  3. Lazy 只能是结构或类的成员。

Lazy 能被捕获吗?

若是你读过个人前一篇文章《Swift 闭包和代理中的循环引用》,你就会明白这个问题。让咱们试一试吧。建立一个名叫 BobGreet 的类,它有两个属性:一个是类型为 Stringname,一个是类型为 String 可是使用闭包建立的 greeting

class BobGreet {
 var name = "Bob the Developer"
 lazy var greeting: String = {
  return "Hello, \(self.name)"
 }()

deinit { 
  print("I'm gone, bruh 🙆")}
 }
}复制代码

闭包可能BobGuest 有强引用,让咱们尝试着 deallocate 它。

var bobGreet: BobGreet? = BobClass()
bobGreet?.greeting
bobClass = nil // I'm gone, bruh 🙆复制代码

不用担忧 [unowned self],闭包并无对对象存在引用。相反,它仅仅是在闭包内复制了 self。若是你对前面的代码声明有疑问,能够读读 Swift Capture Lists 来了解更多这方面的知识。👍

最后的唠叨

我在准备这篇教程的过程当中也学到了不少,但愿你也同样。感谢大家的热情❤️!不过这篇文章还剩一点:个人最后一个目标。若是你但愿加入个人邮件列表以得到更多有价值的信息的话,你能够点 这里注册。

正如封面照片所示,我最近买了 Magic Keyboard 和 Magic Mouse。它们超级棒,帮我提高了不少的效率。你能够在 这儿买鼠标,在 这儿买键盘。我才不会由于它们的价格心疼呢。😓

本文的源码

我将要参加 Swift 讨论会

我将在 6 月 1 日至 6 月 2 日 参加我有生以来的第一次讨论会 @SwiftAveir, 个人朋友 Joao协助组织了此次会议,因此我很是 excited。你能够点这儿了解这件事 的详情!

文章推荐

函数式编程简介 (Blog)

我最爱的 XCode 快捷键 (Blog )

关于我

我是一名来自首尔的 iOS 课程教师,你能够在 Instagram 上了解我。我会常常在 Facebook Page 投稿,投稿时间通常在北京时间上午9点(Sat 8pm EST)。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

相关文章
相关标签/搜索