Swift 函数提前返回
简评:函数提前返回主要的好处是:将每个错误处理进行分离,审查代码时不需要考虑多种复杂异常,我们可以吧注意力集中在也业务逻辑中,调试代码时可以直接在异常中打断点。
提前返回
首先来看一下需要改进的代码示例,我们构建一个笔记应用使用 NotificationCenter API,当笔记内容有变化时 Notification 来通知笔记列表变更,代码如下:
class NoteListViewController: UIViewController {
@objc func handleChangeNotification(_ notification: Notification) {
let noteInfo = notification.userInfo?["note"] as? [String : Any]
if let id = noteInfo?["id"] as? Int {
if let note = database.loadNote(withID: id) {
notes[id] = note
tableView.reloadData()
}
}
}
}
上面的代码可以很好的工作,但是可读性差了点。因为这段代码包含多重缩进和类型转换。我们来尝试改进这段代码。
- 将方法提前返回,让我们函数尽可能的快的返回。
- 使用 guard 替代 if,以避免嵌套。
class NoteListViewController: UIViewController {
@objc func handleChangeNotification(_ notification: Notification) {
let noteInfo = notification.userInfo?["note"] as? [String : Any]
guard let id = noteInfo?["id"] as? Int else {
return
}
guard let note = database.loadNote(withID: id) else {
return
}
notes[id] = note
tableView.reloadData()
}
}
将函数提前返回能够将功能失败的情况处理得更加清晰,这不仅提高了可读性(更少的缩进,更少的嵌套),同时也有利于单元测试。
我们可以进一步改进代码,将获取 noteID 和类型转换的代码放在 Notification Extension 中,这样就将 handleChangeNotification 业务逻辑和具体细节分离开来。修改后代码如下所示:
private extension Notification {
var noteID: Int? {
let info = userInfo?["note"] as? [String : Any]
return info?["id"] as? Int
}
}
class NoteListViewController: UIViewController {
@objc func handleChangeNotification(_ notification: Notification) {
guard let id = notification.noteID else {
return
}
guard let note = database.loadNote(withID: id) else {
return
}
notes[id] = note
tableView.reloadData()
}
}
这种结构还大大简化了调试的难度,我们可以直接在每个 guard 中 return 中添加断点来截获所有失败情况,而不需要单步执行所有逻辑。
条件构造
当构造一个对象实例,非常普遍的需求是需要构建哪类对象取决于一系列的条件。
例如,启动应用程序时显示哪个 view controller 取决于:
- 是否已经登录。
- 用户是否已经完成入职流程(onboarding flow)。
我们对这些条件的的实现可能是一系列的 if 和 else 语句,如下所示:
func showInitialViewController() {
if loginManager.isUserLoggedIn {
if tutorialManager.isOnboardingCompleted {
navigationController.viewControllers = [HomeViewController()]
} else {
navigationController.viewControllers = [OnboardingViewController()]
}
} else {
navigationController.viewControllers = [LoginViewController()]
}
}
同样的提前返回和 guard 语句可以提升代码可读性,但是现在这种情况不是处理失败情况,而是在不同条件下构建不同 view controller。
现在来改进这段代码,使用轻量级的工程模式,将构造初始界面移动到专门的函数中,该函数返回匹配条件的view controller。如下所示:
func makeInitialViewController() -> UIViewController {
guard loginManager.isUserLoggedIn else {
return LoginViewController()
}
guard tutorialManager.isOnboardingCompleted else {
return OnboardingViewController()
}
return HomeViewController()
}
func showInitialViewController() {
let viewController = makeInitialViewController()
navigationController.viewControllers = [viewController]
}
由于 makeInitialViewController 方法是个纯函数(不影响外部状态,固定输入能够得到固定输出),实际上影响外部状态的只有一个地方 navigationController.viewControllers = [viewController] ,(在日常开发中状态如果没有得到很好的控制很容易引起 bug,所以使用更少状态和减少对状态的修改可以一定程度上减少 bug 出现的几率)。
条件控制
最后我们来看看,函数如何简化复杂的条件逻辑。我们来构建一个 view controller 来显示社交应用的评论功能,如果满足三个条件则运行用户对评论进行编辑。代码如下:
class CommentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if comment.authorID == user.id {
if comment.replies.isEmpty {
if !comment.edited {
let editButton = UIButton()
...
view.addSubview(editButton)
}
}
}
...
}
}
这里使用了 3 个 if 嵌套逻辑,每次重新审查代码都会比较困扰,更具之前的经验我们可以对代码进行优化,添加 Comment extension:
extension Comment {
func canBeEdited(by user: User) -> Bool {
guard authorID == user.id else {
return false
}
guard comment.replies.isEmpty else {
return false
}
return !edited
}
}
class CommentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if comment.canBeEdited(by: user) {
let editButton = UIButton()
...
view.addSubview(editButton)
}
...
}
}
Swift 函数提前返回的更多相关文章
- Swift函数编程之Map、Filter、Reduce
在Swift语言中使用Map.Filter.Reduce对Array.Dictionary等集合类型(collection type)进行操作可能对一部分人来说还不是那么的习惯.对于没有接触过函数式编 ...
- 4 .Swift函数|闭包
在编程中,我们常把能完成某一特定功能的一组代码,并且带有名字标记类型叫做函数,在C语言中,我们知道函数名就是一个指针,它指向了函数体内代码区的第一行代码的地址,在swift中也具有同样的功效. 在Sw ...
- Swift 函数
1: 函数形式: Swift函数以关键字func 标示.返回类型->后写明.如果没有返回类型可以省去.多个参数用,分割.其中参数名字在前:类型描述 func GetName(strName:St ...
- Swift函数|闭包
在编程中,我们常把能完成某一特定功能的一组代码,并且带有名字标记类型叫做函数,在C语言中,我们知道函数名就是一个指针,它指向了函数体内代码区的第一行代码的地址,在swift中也具有同样的功效. 在Sw ...
- javascript . 03 函数定义、函数参数(形参、实参)、函数的返回值、冒泡函数、函数的加载、局部变量与全局变量、隐式全局变量、JS预解析、是否是质数、斐波那契数列
1.1 知识点 函数:就是可以重复执行的代码块 2. 组成:参数,功能,返回值 为什么要用函数,因为一部分代码使用次数会很多,所以封装起来, 需要的时候调用 函数不调用,自己不会执行 同名函数会覆盖 ...
- python10--函数的来源,优点,定义,组成,使用(定义,调用)函数的分类,函数的返回值
一.函数 # *****# 函数:完成 特定 功能的代码块,作为一个整体,对其进行特定的命名,该名字就代表函数# -- 现实中:很多问题要通过一些工具进行处理 => 可以将工具提前生产出 ...
- day10函数,函数的使用,函数的分类,函数的返回值
函数 # ***** # 函数:完成 特定 功能的代码块,作为一个整体,对其进行特定的命名,该名字就代表函数 # -- 现实中:很多问题要通过一些工具进行处理 => 可以将工具提前生产出来并命名 ...
- Swift2.0语言教程之函数的返回值与函数类型
Swift2.0语言教程之函数的返回值与函数类型 Swift2.0中函数的返回值 根据是否具有返回值,函数可以分为无返回值函数和有返回值函数.以下将会对这两种函数类型进行讲解. Swift2.0中具有 ...
- swift 函数.和匿名函数
函数 注意: 没有定义返回类型的函数会返回特殊的值,叫 Void.它其实是一个空的元组(tuple),没有任何元素,可以写成(). 使用元组作为返回参数,返回多个参数 func count(strin ...
随机推荐
- 05-了解activiti目录结构
数据库底层支持的23张表的增删改查 如果你不会用activiti的API,可以看一下开发文档 流程引擎ProcessEngines,最重要是这个玩意. libs就更重要了,我们使用一个框架主要是使用它 ...
- linux之badblocks命令
简介 该命令用来检测硬盘坏道.硬盘坏道问题,如忽视,会随着使用而扩大面积,严重损坏硬盘.一般采用检测坏道,进而屏蔽重分区的方式复用硬盘. 语法 badblock(选项)(参数) -b<区块大小& ...
- 链接ftp,把文件或图片上传到ftp指定的文件夹中
/******************************************************************** * * * Filename : .java * Auth ...
- python之selenium调用js(execute_script)
转载: http://www.cnblogs.com/fnng/p/3230768.html 本节重点: 调用js方法 execute_script(script, *args) 在当前窗口/框架 同 ...
- Java程序设计16——Annotatio注释
Annotation是代码里的特殊标记,这些标记可以在编译.类加载.运行时被读取,并执行相应的处理.通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息.代 ...
- 2014 ACM/ICPC 鞍山赛区网络赛(清华命题)
为迎接10月17号清华命题的鞍山现场赛 杭电上的题目 Biconnected(hdu4997) 状态压缩DP Rotate(hdu4998) 相对任一点的旋转 Overt(hdu4999 ...
- Android targetSdkVersion 原理
前几天 Google 官方发布文章解析 compileSdkVersion.minSdkVersion 以及 targetSdkVersion 的含义,以及合理设置各个值的意义,原文 Picking ...
- jquery中prop()和attr()的区别
相比attr,prop是1.6.1才新出来的,两者从中文意思理解,都是获取/设置属性的方法(attributes和properties).只是,window或document中使用.attr()方法在 ...
- UVaLive 4128 Steam Roller (多决策最短路)
题意:给定一个图,r 根横线, c 根竖线.告诉你起点和终点,然后从起点走,每条边有权值,如果是0,就表示无法通行.走的规则是:如果你在下个路要转弯,会使这段路的时间加倍,但是如果一条路同时是这样,那 ...
- Git简明使用指南[转]
git - 简易指南 助你开始使用 git 的简易指南,木有高深内容,;). Tweet 作者:罗杰·杜德勒 感谢:@tfnico, @fhd and Namics 其他语言 english, deu ...