Swift:在闭包中进行强捕获的两种方式

69 阅读2分钟

在Swift中,有两种方法可以将self ,作为转义闭包中的强引用。第一种是当我们在这样一个闭包中调用当前对象的方法或访问其属性时,明确地使用self 关键字。

例如,下面的VideoViewController 执行了这样的强捕获,以便在它完成准备其Video 模型时能够调用它自己的两个方法:

class VideoViewController: UIViewController {
    private var video: Video
    ...
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        prepareVideo {
            self.movePlayhead(to: self.video.lastPlayheadPosition)
            self.startPlaybackIfNeeded()
        }
    }
}

在上面的例子中,强捕获self 是安全的,原因是我们传递给prepareVideo 的闭包并没有以可能导致保留循环的方式来存储。关于这个话题的更多信息,请查看"在使用闭包时,是否总是需要使用[弱自]?"。

以上当然是在转义闭包中访问self 上的属性和方法的最著名的方法。但还有另一种鲜为人知的技术,可以让我们只引用一次self --那就是使用捕获列表来设置我们的引用。虽然捕获列表最常用于我们想要创建一个弱的或不属于自己的self引用,或者当我们想要捕获一组特定的属性时,它们也可以用于像上面这样的情况,以避免多次重复输入相同的self 前缀--像这样:

class VideoViewController: UIViewController {
    private var video: Video
    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        prepareVideo { [self] in
            movePlayhead(to: video.lastPlayheadPosition)
            startPlaybackIfNeeded()
        }
    }
}

请注意,我们现在可以在self 上调用方法和访问属性,就像我们在转义闭包以外的上下文中编写代码一样,这很整洁

当然,我们也可以选择将我们想要执行的逻辑转移到一个专门的方法中,然后我们可以在闭包中简单地调用这个方法:

class VideoViewController: UIViewController {
    private var video: Video
    ...

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        prepareVideo {
            self.videoWasPrepared()
        }
    }
    
    private func videoWasPrepared() {
        movePlayhead(to: video.lastPlayheadPosition)
        startPlaybackIfNeeded()
    }
}

需要重申的是,上述技术是专门用于创建对象引用。当我们不想保留当前对象时,我们最好使用弱的或无主的self 捕获,或者完全避免捕获self