Swift 中的关键字
Table of Contents
熟悉一门语言,可以从熟悉它的关键字入手。
本文列举一下 Swift 区别 OC 的一些特有关键字吧。这里仅列举常用的,完整的关键词列表可以参考: Swift 语言参考/关键字和标点符号
@autoclosure #
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
—— Swift 官方教程
自动闭包,也是一种闭包。
自动就是省略了 {}
,系统自动将一个表达式转换成闭包。有个前提是这个闭包不能有参数。否则会收到编译错误:
Argument type of @autoclosure parameter must be ‘()’
private func _autoclosure(_ bar: @autoclosure () -> Bool) {
if bar() {
print(#function)
}
}
闭包是个值得讨论的话题,有机会单独写一篇文章。
@escaping #
逃溢闭包,是指闭包的生命周期被延长。正常情况下在闭包作为函数参数时,函数执行完,闭包就被释放了。
但是,如果闭包被缓存或延迟执行,生命周期被延长到函数执行之后。就需要显示指明 @escaping
,否则会编译报错。
因为 Swift 3 以后,闭包默认都是 nonescape
的。
Escaping closure captures non-escaping parameter ‘bar’
private func _escapingclosure(_ arg1: Int, _ arg2: Int, _ bar: @escaping (_ a: Int, _ b: Int) -> Int) {
let concurrentQueue = DispatchQueue(label: "com.ryder.concurrent.queue1", attributes: .concurrent)
concurrentQueue.asyncAfter(wallDeadline: .now() + 1.5) {
print(bar(arg1, arg2))
}
}
@convention #
也是用于修饰闭包的
@convention(c)
修饰的闭包会返回 C 类型的函数指针,用于传给参数是函数指针的 C 方法。
@convention(block)
修复的闭包会返回 block,用于传给参数是 block 的 OC 方法。
defer #
defer 后面接一个闭包,表示推迟这个闭包执行到 defer 语句的作用域之后。
private func _defer() {
var num = 1
defer { print("Defer: \(num)") }
num += 1
print("Number: \(num)")
}
// Number: 2
// Defer: 2
defer 内的闭包不会持有外部值。
More: 喵总遇到的 defer 死锁问题
deinit #
deinit {
print(String(format: "%@ dealloced.", String(describing: self)))
}
析构方法。
extension #
扩展,类似于 OC 的 category。
扩展的作用主要有:
- 添加计算实例属性和计算类型属性
- 定义实例方法和类型方法
- 提供新初始化器
- 定义下标
- 定义和使用新内嵌类型
- 使现有的类型遵循某协议
类型方法的定义,所有类型都可以有类型方法,不限于 class/struct/enum。都可以使用
static func
, 而在 class 中还可以使用class func
。
注意:扩展不能直接定义存储类属性,也就是不能有属性的实例。也没有 OC 的关联对象可以使用 (当然我们可以桥接一个 OC 实现)。
那么怎么办呢?有个取巧的办法是定义一个类变量。
extension MyOCFunction {
private static var _temp = 0
var fact: Int {
get {
return MyOCFunction._temp
}
set {
MyOCFunction._temp = newValue
}
}
}
fallthrough #
贯穿。Swift 中的 case 默认是非贯穿的,也就是 case 执行完之后自动退出,所以也就不需要 break 了。
但是,如果你希望像其他语言一样,多个 case 执行一段逻辑,则可以在 case 后使用 fallthrough
fileprivate #
Swift 共有五种访问控制关键字,从高到低:
open
, 可以被本模块和外部模块任意访问public
, 与 open 唯一的区别是不能被外部模块继承和重写 (can’t subclass or override outside the module)internal
, 默认 的级别,本模块内任意访问,不能被外部访问fileprivate
, 当前文件可见private
, 实体作用域可见,对于属性来说,仅当前类和它的 externsion 可见
这几个关键字可以用于修饰实体,如属性、基本类型、函数等。
final #
- final 修饰类,表示这个类不对被继承 (不能修饰 struct/enum,因为它们本来就不会被继承)
- final 修饰类中的属性、方法,表示不能被子类重写
- 显然,final 和 open 关键字是互斥的
guard #
guard 守卫,用于校验某个条件,并退出当前作用域。
在 guard..else
块中必须使用 return/throw
显式退出。
let temp = 0
guard temp != 0 else {
print("zero checked.")
return
}
inout #
inout 用于在函数内修改值类型数据。在调用时,需要在参数前加 &
var num = 1
self._inout(num: &num)
private func _inout(num: inout Int) {
num *= 2
}
inout 的工作方式是这样的(copy-in-copy-out):
- 函数调用时,参数的值被拷贝。
- 函数体内部,拷贝后的值被修改。
- 函数返回后,拷贝后的值被赋值给原参数。
let/var #
let - constant 修饰常量,仅可赋值一次 var - variable 修饰变量,可以被改变
不管是常量还是变量,定义都遵循标识符的 定义标准,我们可以使用各种 Unicode 字符包括 emoji 来定义名称。
private func _var() {
var 🐶🐮 = "dogcow";
🐶🐮 = "cowdog"
print(🐶🐮)
let 💩 = "shit"
print(💩)
}
lazy #
lazy 用在属性中,表示这个属性只有在被访问到才会执行初始化。也就是所谓的“懒加载”,避免的无效的内存占用。
另一种用法,在序列,如 Array 的计算时,可以使用 lazy 只计算关心的值,如:
// 只输出两次 `Calculating..`
private func _lazy () {
let array = Array(0..<100)
let incArray = array.lazy.map{(item) -> Int in
print("Calculating..")
return item + 1
}
print(incArray[0], incArray[8])
}
如果没有 lazy
,那么 map 会被调用 100 次。
ref: https://swift.gg/2016/03/25/being-lazy/
mutating #
对于 struct/enum 值类型对象来说,使用 mutating 修饰的方法才能修改它们的属性。
struct Meeting {
var name : String
var date : Date?
init() {
self.name = ""
self.date = Date()
}
mutating func cancel() {
self.date = nil
}
}
Optional #
可选类型。
这是 Swift 区别 OC 的一个主要特征。
prefix/infix/postfix #
用于修饰自定义操作符的,prefix 表示操作符在变量前,同理 infix 和 postfix 分别是中缀表达式和后缀表达式。
自定义操作符方法,必须是 static
的。
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
struct/class #
结构体与类的区别,在 之前 已经介绍过,不再赘述。
tuple #
元组,通过圆括号和分号,将不同的类型组成在一起,形成一个新的类型。tuple 甚至可以嵌套包含别的 tuple。
A tuple type is a comma-separated list of zero or more types, enclosed in parentheses.
通常用于函数返回值。
一个有趣的事实:
Void
是 ()
的 typealias
,其实就是一个空的 tuple
typealias #
Swift 中替代 typedef
的关键字。