另一种写 strongSelf 的方式

1,334 阅读2分钟

大家有没有遇到过这种情况, 接手了一份代码, 然后发现里面闭包循环引用的问题很严重, 而所有代码都长成这个样子:

request(url).responseModel { response in
    guard let model = response.result.value else {
        return
    }

    self.int = model.int
    self.label.text = self.string + model.string
    ...
    ...
}

而我们常规的做法是声明为 weak self, 然后再用 strongSelf 去解包:

request(url).responseModel { [weak self] response in
    guard let model = response.result.value else {
        return
    }
    guard let strongSelf = self else {
        return
    }

    strongSelf.int = model.int
    strongSelf.label.text = strongSelf.string + model.string
    ...
    ...
}

这种做法最烦的一点就是必须把所有的 self 都改成 self? 或者 strongSelf , 而 Swift 目前重构工具又不完善, 就剩 AppCode 的还能稍微用一用, 还是说自己写正则? 或者用 Vim 之类的快捷操作? (不过讲真, 如果项目大还真的可以写个正则…)

最开始遇到这个问题的时候, 可能会有一部分在想, 能不能声明一个本地变量 self 去解包, 就没那么多麻烦的事情了

guard let self = self else { // 报错
    return
}

但这么做却会报错, 编译器没办法声明为本地变量, 刚开始的时候我以为是因为 self 的声明范围是在 block 内部, 同一作用域内没办法声明两个同名变量, 我就没有太留意, 都是用 strongSelf 去解包.

直到有一天在看 ModelMapper 的源代码的时候, 发现了一种新的写法:

guard let `self` = self else {
    ...
}

原来是因为 self 是 Swift 的关键字, 所以我们使用时默认为 block 捕获的 self, 只要我们用 `` 去包住 self, 告诉编译器我们不是在使用关键字就可以了. 这么写就可以相对快速地解决内存泄露的问题.

不过其实 strongSelf 会很明显地告诉你这是解包后的 self, 我虽然还没发现有导致 bug 的可能性, 但还是觉得这么做可能会出现问题, 所以在实际项目里, 我最后还是把所有的解包都改成了 strongSelf.

下篇文章我再给大家介绍一个奇技淫巧...

觉得我写的还 ok 的朋友可以关注一下我的博客