Swift 冷门语法知识

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

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

带关联值的 Enum 的构造器

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

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 的时候有用到过它的构造器,查阅以后,发现正确的作法应该是这样的:swift

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 的另外一种写法

以前我就写过一篇文章来说这个,之因此再提一次,一方面是为了文章的完整性,另外一方面就是为了下文的另外一个语法作铺垫。app

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

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 是系统关键字,使用 ` 包住关键字,可让编译器把它看作是一个正常的变量名称。spa

而后咱们在闭包里使用 self 时,就没必要考虑它是否会产生循环引用的问题,别的地方的代码也能够很方便地复制粘贴过来,不用把 self 所有都改成 strongSelfcode

出处:忘了😒

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 的简洁。

以为文章还不错的话能够关注一下个人博客

相关文章
相关标签/搜索