Swift 冷门语法知识

本来这篇文章的标题是“如何写一个不安全的构造器”,但后面查资料的时候又发现了一些很好玩的东西,就一次性写成一篇出来,跟大家分享一下 Swift 里的几个 best pratice:

  • 带关联值的 Enum 的构造器
  • strongSelf 的另一种写法
  • 如何在 Swift 里写一个不安全的构造器
  • mutating 函数的定义

带关联值的 Enum 的构造器

写 Swift 的人应该很熟悉带关联值的枚举(Enumeration with Associated Value),例如原生的 Optional,错误处理的 Result 库等等,但在我尝试自定义枚举的构造器时遇到了这样的问题:

enum CustomOptional<Wrapped> {
case value(Wrapped)
case none

init(value: Wrapped) {
return .value(value)
// error: 'nil' is the only return value permitted in an initializer
}
}

错误提示是构造器里只能够返回 nil,但如此一来我们就好像没有办法把构造器实现出来了。我想起在使用 Result 的时候有用到过它的构造器,查阅之后,发现正确的做法应该是这样的:

enum CustomOptional<Wrapped> {
case value(Wrapped)
case none

init(value: Wrapped) {
self = .value(value)
}
}

顺带说一句,所有的值类型都支持这种写法。

出处:Result — Swift type modelling the success/failure of arbitrary operations

strongSelf 的另一种写法

之前我就写过一篇文章来讲这个,之所以再提一次,一方面是为了文章的完整性,另一方面就是为了下文的另一个语法做铺垫。

从 OC 带过来的命名方式,会让我们在闭包里这么去写 strongSelf:

block = { [weak self] in
guard let strongSelf = self else { return }
... other code ...
}

strongSelf 在代码里的出现其实会有点突兀,我会更喜欢利用 Swift 一种语法,让代码变得统一:

block = { [weak self] in
guard let `self` = self else { return }
... other code ...
}

这里声明了一个局部变量 self,让我们可以直接用来将捕获的 weak self 解包出来,由于 self 是系统关键字,使用 ` 包住关键字,可以让编译器把它看做是一个正常的变量名称。

然后我们在闭包里使用 self 时,就不必考虑它是否会产生循环引用的问题,**别的地方的代码也可以很方便地复制粘贴过来,不用把 self 全部都改为 strongSelf**。

出处:忘了😒

Update 2017.08.24:

掘金里有位大神在评论里提醒我,原来这是一个编译的 bug,提案 SE-0079 很详细地讲了这件事情,但目前这个 bug 还没有修复,按照上面的方法去写就可以了。

如果这个 bug 被修复了的话,就可以没必要加上 `,可以直接声明局部变量 self:

block = { [weak self] in
guard let self = self else { return }
... other code ...
}

如何在 Swift 里写一个不安全的构造器

开头我提到了这篇文章原本的标题是叫做“如何写一个不安全的构造器”,其实我是在写这篇文章的时候才发现了上面的语法,之前我是用了另外一种比较 dirty 的方式去做的:

enum CustomOptional<Wrapped> {
case value(Wrapped)
case none

static func `init`<Wrapped>(value: Wrapped) -> CustomOptional<Wrapped> {
return .value(value)
}
}

很早的时候我就尝试过定义一个名为 init 的 static 函数,得到的是这样的提示 error: keyword 'init' cannot be used as an identifier here,也就是说 init 作为系统关键字不能在这里使用,那么很简单,用 ` 把它包住就行了。

这么定义 init 方法的话,在调用时也可以像正常的构造器那样省略掉 init:

let _ = CustomOptional(value: "I'm a String")

这种“构造器”的定义和实现都很灵活,可以返回任何类型,内部实现也不需要遵守那么多规则。这可能在一些我意想不到的场景下会有用吧,但我暂时没有想到,如果你恰好用到了这个小技巧,请务必发个邮件告诉我,我很好奇具体的使用场景。

出处:kemchenj

mutating 函数的定义

定义值类型的时候,同一个函数,我们经常需要定义 mutating 和 non-mutating 两个版本:

func sorted() -> Array { ... }

mutating func sort() { ... }

但绝大部分情况下这两个函数的实现基本上都是一样的,这个时候我们就可以考虑复用其中一个,减少重复代码:

func sorted() -> Array { ... }

mutating func sort() {
self = sorted()
}

之所以可以这样写,是因为 mutating 意味着函数会对值自身进行修改:

self.property = value

// 等价于

var newStruct = self
newStruct.property = value
self = newStruct

出处:Swift Talk #21 Structs and Mutation

最后

我想推荐一下这个视频,主要是讲 Swift 里如何构建高效的 Collection 类型,20分钟的长度,看完之后对于 objc.io 的那本书动心了,我基础很差也基本上看懂了里面的内容,讲得真的很不错,里面平衡二叉树的实现让我再一次强烈地感受到 Swift 的简洁。