Swift Tips Part I
本文主要记录一些自己在Swift的学习过程中遇到的一些小知识点,以便加深记忆
如何给代码分段
熟悉Objective-C的同学都知道可以通过#pragma mark escription 宏标记,在代码的方法导航中添加描述语句。此外,还可以在描述语句前添加一条中划线,例如#pragma mark - Description,这样在方法导航中,会显示一条横线将代码段分隔开,令分段显示更加清晰。
那么怎么在Swift中使用呢?可以通过//MARK: description,//????: description ,//FIXME: description 这样的注释来添加分段说明,同样的,也可以在冒号后添加中划线,例如://MARK: - description,//????: - description ,//FIXME: - description ,将代码分段用横线隔开。
如何在Swift中使用保留关键字
通常一种编程语言都是不允许使用关键字作为变量名的,那么在SWift中如果非要用到关键字怎么办呢?可以通过用``将关键字括起的方式来使用,例如:`self`,当然,这是最后的手段,但有一线希望都不应该用关键字来作为变量名。
几个常见数值字面量
- 十进制 
let decimalInteger = 17 - 二进制 
let binaryInterger = 0b10001等价于17 - 八进制 
let octalInteger = 0o21等价于17 - 十六进制 
let hexadecimalInteger = 0x11等价于17 - 科学计数法 
1.25e2等价于125.0;1.25e-2等价于0.0125 - 十六进制指数式 
0xFp2表示$15 * 2^2$,0xFp-2表示$15 * 2^-2$ - 十进制格式增强 
let paddedDobule = 000123.456,let oneMillion = 1_000_000,let justOverOneMillion = 1_000_000.000_000_1 
lazy属性的线程安全问题
我们知道 Swift 通过其语言自身的特点,保证了全局常量和存储型属性,即使被多个线程交替存取,也仅初始化一次,但是这里有一个特例,就是 lazy 属性,当多个线程同时访问一个尚未初始化的 lazy 属性时,则不能保证仅初始化一次。
如何便捷的在Swift中获取指针
Swift虽然在极力避免指针,但是为了 Object-C 和 C 兼容,还是保留了指针,但是我们不能像在 C 中一样通过&便捷的获取某个常量或变量的指针,例如:
1  | let a = 8  | 
查看下错误信息:”Type ‘inout Int’ of variable is not materializable”,这里编译器将&a识别为 inout Int类型,并不是我们期望的指针类型,所以是不是我们只要把它明确指定为指针类型就可以了呢?我们来试一下:
1  | func converToUnsafePointer(_ pointer: UnsafePointer<Int>) -> UnsafePointer<Int> {  | 
哈哈,成功了,顺利通过编译。
Swift错误处理原则
来源:Magical Error Handling in Swift,由 Gemma Barlow 发表于Raywenderliche
- 错误类型的命名要清晰无歧义
 - 单个错误尽量用可选类型来处理
 - 当可能出现多种错误时,用自定义的错误类型来处理
 - 不要让抛除的错误传播过远
 
Swift数组指针的妙用
1  | let numbers = [1, 2, 3, 4, 5]  | 
简写闭包参数名
来源《The Swift Programming Language (SWift 3.0.1)》Closure 一章Shorthand Argument Names 小节
Swift 自动为内联闭包提供了依次代表代表参数值的$0,$1,$2等参数简写。
如果您在闭包表达式中使用参数名称简写,您可以在定义闭包时省略其参数列表,相应参数简写的类型会通过函数类型对其进行推断。此外in关键字也可以被省略,因为此时闭包表达式完全由闭包函数体构成:
1  | reversed = sorted(names, { $0 > $1 } )  | 
在这个例子中,$0和$1分别表示闭包中第一个和第二个String类型的参数。
绘制虚线
1  | if let context = UIGraphicsGetCurrentContext() {  | 
访问控制
Swift 3.0更新了访问控制,添加了open和fileprivate两个新控制权限,这里摘录了《The Swift Programming Language (Swift 3)》书中部分内容:
Swift 为代码中的实体提供了5种不同的访问权限。访问权限同时取决于源文件中的实体定义及源文件所属模块。
- open、public 访问权限定义的实体,在同一模块内可于任意源文件内访问,即使该源文件属于另一个被引入的模块。open 和 public 的区别稍后介绍。
 - internal 访问权限定义的实体,可以被其定义的模块内的任意源文件访问,模块外的源文件则不能。通常,定义一个 app 或 Framework 的内部结构时,会使用internal权限。
 - fileprivate 访问权限,限制实体仅能在其定义的源文件内访问。当某个功能仅用于某个源文件时,我们就可以通过添加 fileprivate 来隐藏其具体的实现细节。
 - private 访问权限定义的实体,仅能在其所定义的作用域内访问。当某个功能仅用于某个声明时,我们就可以通过 private 访问权限,隐藏其具体的实现细节。
 
open是最高访问权限(最少限制),private是最低访问权限(限制最多)。
open访问权限仅适用于类和类成员,它与public访问权限的区别如下:
- 定义为 public,或其它更严格访问权限的类,仅能在其所定义的模块内被继承。
 - 定义为 public,或其它更严格访问权限的类成员,仅能在其所定义的模块内被override(覆盖、重写)。
 - 定义为 open 访问权限的类,既可以在其所定义的模块内被继承,也可以在其它引入其被定义模块的模块中被继承。
 - 定义为 open 访问权限的类成员,既可以在其所定义的模块内override(覆盖、重写),也可以在其它引入其被定义模块的模块中override(覆盖、重写)。
(译者:在a模块中用 open 定义了一个A类,那么A类既可以在a模块中被继承,也可以在任何引入了a模块的b,c…等模块中被继承) 
将一个类标记为 open 访问权限,即表明你已充分考虑到该类会被其它模块当作父类继承,并为此妥善的编写了该类的代码。
@objc & dynamic
关键字,摘录自王巍 《Swift tips》
@objc 当 Objective-C 需要引用 Swift 代码,我们可以将需要暴露给 Objective-C 使用的任何地方 (包括类,属性和方法等) 的声明前面加上 @objc 修饰符。
@objc 修饰符的另一个作用是为 Objective-C 重新声明方法或者变量的名字
@dynamic 当需要用刀某些动态特性时,即可用此关键词,例如KVO。
@escaping & @noescape
摘录自《What Do @escaping and @noescape Mean In Swift 3》
这里作者介绍的非常详细,且是完整文章,所以不在狗尾续貂。
溢出运算符
当向一个整型常量或变量赋一个超出其所允许范围的数值时,默认情况下,Swift不会生成一个无效的数值,而是报错。该做法为过大或过小数值地操作提供了额外的安全性。
例如,Int16 整型能容纳从 -32768 到 32767 的有符号整型。如果尝试向 Int16 有符号整型的常量或变量赋超过其容纳范围的数值,则会报错:
1  | var potentialOverflow = Int16.max  | 
在处理过大或过小值时提供相应的错误处理,可以让我们对边界的操作更加灵活。相对于报错,我们可以对数值进行截取。Swift提供了三种溢出运算符来让系统支持整型溢出运算。这些运算符均以 & 开头:
- 溢出加 &+
 - 溢出减 &-
 - 溢出乘 &*
 
特殊的字面量
在 C 中调试时我们经常会用到 __FILE__,__FUNCTION__ 等特殊字面量,来获取一些信息,那么在 Swift 中怎么用呢?
1  | print(#file)// 打印当前文件  | 
通过上面的例子可以看出,Swift 采用 # + 关键字小写的方式,很好的照顾了那些从其它语言转过来的朋友们。
用函数做参数以简化代码
摘录自:@南峰子_老驴
1  | let setInt: (Int, String) -> Void = UserDefaults.standard.set  | 
包名冲突
今天在尝试项目中引入“NetworkExtension”的时候,发现编译器提示“File ‘NameOfCrrentFile.Swift is a part of module ‘NetworkExtension’ ; ignoring import”, 后来在苹果官方论坛上找到了答案,原来是工程名“NetworkExtension”与要引入的包名冲突了,由于**.swift已经是“NetworkExtension”工程(实际也是一个包)一部分,所以Swift拒绝再在该包中引入一个同名的包。
Swift虽然支持命名空间,但是随即也带来了类似的问题,看来接下来有必要好好了解下Swift的包管理和命名空间问题。
#keyPath语法糖
摘录自:@南峰子_老驴
在使用KVC或KVO时,经常会犯一些因KeyPath拼写不正确,从而导致应用崩溃的错误。为此,Swift 3中引入了 #keyPath()表达式,不多解释,直接看代码:
1  | class Person: NSObject {  |