Swift函数提前返回实例详解
简评:
函数是一个组织在一起语句集合,以执行特定任务。Swift函数类似于简单C函数以及复杂的ObjectiveC语言函数。它使我们能够通过函数调用内部的局部和全局参数值。像其他任何语言一样swift函数也遵循相同的步骤。
- 函数声明:它告诉编译器有关的函数的名称,返回类型和参数。
- 函数定义:它提供函数的实际主体。
Swift函数包含参数类型和返回类型。
函数提前返回主要的好处是:将每个错误处理进行分离,审查代码时不需要考虑多种复杂异常,我们可以吧注意力集中在也业务逻辑中,调试代码时可以直接在异常中打断点。
提前返回
首先来看一下需要改进的代码示例,我们构建一个笔记应用使用NotificationCenterAPI,当笔记内容有变化时Notification来通知笔记列表变更,代码如下:
classNoteListViewController:UIViewController{ @objcfunchandleChangeNotification(_notification:Notification){ letnoteInfo=notification.userInfo?["note"]as?[String:Any] ifletid=noteInfo?["id"]as?Int{ ifletnote=database.loadNote(withID:id){ notes[id]=note tableView.reloadData() } } } }
上面的代码可以很好的工作,但是可读性差了点。因为这段代码包含多重缩进和类型转换。我们来尝试改进这段代码。
- 将方法提前返回,让我们函数尽可能的快的返回。
- 使用guard替代if,以避免嵌套。
classNoteListViewController:UIViewController{ @objcfunchandleChangeNotification(_notification:Notification){ letnoteInfo=notification.userInfo?["note"]as?[String:Any] guardletid=noteInfo?["id"]as?Intelse{ return } guardletnote=database.loadNote(withID:id)else{ return } notes[id]=note tableView.reloadData() } }
将函数提前返回能够将功能失败的情况处理得更加清晰,这不仅提高了可读性(更少的缩进,更少的嵌套),同时也有利于单元测试。
我们可以进一步改进代码,将获取noteID和类型转换的代码放在NotificationExtension中,这样就将handleChangeNotification业务逻辑和具体细节分离开来。修改后代码如下所示:
privateextensionNotification{ varnoteID:Int?{ letinfo=userInfo?["note"]as?[String:Any] returninfo?["id"]as?Int } } classNoteListViewController:UIViewController{ @objcfunchandleChangeNotification(_notification:Notification){ guardletid=notification.noteIDelse{ return } guardletnote=database.loadNote(withID:id)else{ return } notes[id]=note tableView.reloadData() } }
这种结构还大大简化了调试的难度,我们可以直接在每个guard中return中添加断点来截获所有失败情况,而不需要单步执行所有逻辑。
条件构造
当构造一个对象实例,非常普遍的需求是需要构建哪类对象取决于一系列的条件。
例如,启动应用程序时显示哪个viewcontroller取决于:
- 是否已经登录。
- 用户是否已经完成入职流程(onboardingflow)。
我们对这些条件的的实现可能是一系列的if和else语句,如下所示:
funcshowInitialViewController(){ ifloginManager.isUserLoggedIn{ iftutorialManager.isOnboardingCompleted{ navigationController.viewControllers=[HomeViewController()] }else{ navigationController.viewControllers=[OnboardingViewController()] } }else{ navigationController.viewControllers=[LoginViewController()] } }
同样的提前返回和guard语句可以提升代码可读性,但是现在这种情况不是处理失败情况,而是在不同条件下构建不同viewcontroller。
现在来改进这段代码,使用轻量级的工程模式,将构造初始界面移动到专门的函数中,该函数返回匹配条件的viewcontroller。如下所示:
funcmakeInitialViewController()->UIViewController{ guardloginManager.isUserLoggedInelse{ returnLoginViewController() } guardtutorialManager.isOnboardingCompletedelse{ returnOnboardingViewController() } returnHomeViewController() } funcshowInitialViewController(){ letviewController=makeInitialViewController() navigationController.viewControllers=[viewController] }
由于makeInitialViewController方法是个纯函数(不影响外部状态,固定输入能够得到固定输出),实际上影响外部状态的只有一个地方navigationController.viewControllers=[viewController],(在日常开发中状态如果没有得到很好的控制很容易引起bug,所以使用更少状态和减少对状态的修改可以一定程度上减少bug出现的几率)。
条件控制
最后我们来看看,函数如何简化复杂的条件逻辑。我们来构建一个viewcontroller来显示社交应用的评论功能,如果满足三个条件则运行用户对评论进行编辑。代码如下:
classCommentViewController:UIViewController{ overridefuncviewDidLoad(){ super.viewDidLoad() ifcomment.authorID==user.id{ ifcomment.replies.isEmpty{ if!comment.edited{ leteditButton=UIButton() ... view.addSubview(editButton) } } } ... } }
这里使用了3个if嵌套逻辑,每次重新审查代码都会比较困扰,更具之前的经验我们可以对代码进行优化,添加Commentextension:
extensionComment{ funccanBeEdited(byuser:User)->Bool{ guardauthorID==user.idelse{ returnfalse } guardcomment.replies.isEmptyelse{ returnfalse } return!edited } } classCommentViewController:UIViewController{ overridefuncviewDidLoad(){ super.viewDidLoad() ifcomment.canBeEdited(by:user){ leteditButton=UIButton() ... view.addSubview(editButton) } ... } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。