iOS SnapKit架构之道(三)巧用Swift

status
category
date
summary
slug
icon
tags
password
前两篇主要介绍了约束从创建到激活的具体过程,简单学习了一小部分比较突出的设计方案,但像SnapKit这种落地到iOS平台Swift语言的开源框架,不仅需要优雅的设计模式,更需要完美地贴合语言特点,并最大化利用语言特性。本文以SnapKit中运用到的Swift特性为切入点,也算是学习Swift的过程了。

条件编译#if

在C系语言中,可以通过#if的分支进行选择性编译,Swift中虽然对宏定义有限制,但依然保留了这个语法,并提供了一些参数选项:
方法
参数选项
os()
iOS,tvOS,OSX
arch()
arm,arm64,i386,x86_64
其中,arm和arm64分别表示32位和64位的真机,i386和x86_64分别代表32位和64的模拟器。例如在SnapKit中,每个文件都会对当前平台进行判断:

类型别名typealias

使用typealias可以为已有类型重新指定名称,以提高代码的可读性。例如在SnapKit中,根据平台的不同,将不同的View类型重命名为通用的名称,在之后的使用中就可以不关心平台:
另外,被重命名的类型必须是确定的,即不能对整体泛型使用typealias:
说到typealias,就不得不提关联类型associatedtypeassociatedtype的使用场景和泛型接口有些类似,如果定义协议时使用了关联类型,使用时就可以不关心它的实际类型,例如在Container协议中:
容器类中的内容可以是任意类,但编写协议中的方法时,需要把容器中的类当做泛型处理。在上面这个例子中,append方法往容器添加一个项目,这个项目可以是任意类型,为了实现这一点,协议添加了关联类型associatedtype Item

闭包closure

个人认为,闭包是一种轻量级的语法,可以看做是能够捕获上下文的匿名函数。因为它能够接收上下文的变量和常量作为参数,并且能够把“执行代码”这个操作以值的形式进行传递,就像是一个没有被声明的函数。闭包的用法和相关知识比较复杂,这里只是想说明SnapKit广泛使用闭包能够带来很多便利之处,例如使用时:
这种闭包称为尾随闭包,如果需要将一个很长的闭包参数传递给函数,可以把闭包放在函数后面以提高代码可读性,而实际上这个方法的声明是常规的,只不过第二个参数是一个闭包:

选项集合OptionSet

在介绍OptionSet之前,先说说位掩码(Mask),位掩码是一串二进制数,可以利用位运算达到屏蔽指定位的目的。Swift给出的位掩码的方案就是OptionSet,它可以提供类似枚举的可选属性,还可以提供各个属性之间的组合。
具体使用时,需要用结构体遵从OptionSet协议,例如SnapKit中的约束属性ConstraintAttributes
除了提供便于位运算的2的整数次幂值,该结构体还提供了rawValue和结构体类型的转换,一般通过传入rawValue构造这个结构体,实际上也是它的init方法。另外,ConstraintAttributes还遵从了ExpressibleByIntegerLiteral协议,意味着它可以通过数组值的方式进行初始化,例如传入包含lefttop的数组(它们的值分别是1和2,二进制分别是01和10),则代表同时选中这两个选项,所以用二进制表示结果就是11,即可以用某位的值表示是否选中某个选项。
OptionSet 和 Enum的区别在于,Enum 适用于各选项互斥的情况,使用时必须从选项中选择一个;而 OptionSet 提供的选项都是可选的,使用时需要判断每个选项是否应该选中。它们的区别有点类似于单选题和多选题的区别。

guard

guard是 Swift 2.0 推出新特性,是一个条件控制语句,它的用法和经典的if else有一定联系,下面举例说明。
例如我们需要对一个整型参数进行判断,当它大于零时进行相关操作,我们可能会这样写:
这是一种基本的 Objective-C 的方式,优先考虑了不符合预期的情况,但是有两个主要的缺点:
  1. 需要人为转换预期条件,例如我们希望 x > 0,但实际上 if 的判断中我们需要编写 x! <= 0 这种代码。当判断条件增多,且不易人为考虑相反情况时,可能会出现问题。
  1. 当 if 判断不符合,即 x 的值在预期范围内时,还需要进行类似于 x! 的拆包操作。
可以利用 Swift 的可选绑定解决第二个问题:
这样就不需要进行拆包操作了,但是引入了另一个问题:这种写法将使用 x 的代码放在了 if 条件判断中,异常情况放在了条件判断之后,这样会导致 if 匹配条件的嵌套,代码的可读性也会降低。
针对这种情况的解决方式就是,对条件逐一检查,只要不符合就退出,这就是 guard 语句:
可见 guard 语句有三个优点:
  1. 检查期望条件,而非异常情况
  1. 通过条件判断后,支持自动拆包
  1. 对不期望的情况早做检查,提高代码的可读性
Loading...

© 刘口子 2018-2025