self 在所有成员初始化之前被闭包捕获 - 但我确实初始化了它们

mat*_*att 5 uitableview ios swift diffabledatasource

这是一个玩具示例,但它准确地减少了我所处的情况:

class MyDataSource: UITableViewDiffableDataSource<String,String> {
    var string : String?
    init(string:String?) {
        self.string = string
        super.init(tableView: UITableView()) { (_, _, _) -> UITableViewCell? in
            print(self.string) // error
            return nil
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试使我的表视图数据源自包含,而我这样做的方式(到目前为止)是将 UITableViewDiffableDataSource 子类化。这工作正常,除非我尝试为我的子类提供自定义初始值设定项。玩具示例显示了问题。

我想要填充单元格的方式绝对取决于在数据源的生命周期以后可以更改的值。因此它不能被硬编码到单元提供者函数中。我不能在这里简单地引用在string初始化程序中传递的值;我必须参考,self.string因为string稍后其他代码将有权更改此数据源的实例属性,并且我希望单元提供程序在发生这种情况时使用该新值。

但是,我收到错误“在所有成员初始化之前被闭包捕获的自我”。这似乎不公平。我确实string在调用之前初始化了我的实例属性super.init。因此,在可能调用单元提供程序方法的最早时刻,它确实具有值。

小智 5

您可以self通过访问tableView.datasource它来解决大部分问题。

  • 真正的错误/API设计问题是 UITableViewDiffableDataSource 需要 CellProvider 闭包作为 init() 参数,但该闭包不能引用 self.anything ...这意味着 CellProvider 不能询问有关数据上下文的任何问题当生成单元格时...这是一个愚蠢的设计。应该能够 init() 对象,然后在 super.init() 之后将 .cellProvider 设置为闭包 (4认同)

Tyl*_*ler 5

虽然我不完全确定为什么Swift 不允许这样做(与self在实际调用之前捕获以创建闭包有关super.init),但我至少知道它的解决方法。改为捕获一个弱局部变量,并在调用后将该super.init局部变量设置为self

class MyDataSource: UITableViewDiffableDataSource<String,String> {
    var string : String?
    init(string:String?) {
        self.string = string
        weak var selfWorkaround: MyDataSource?
        super.init(tableView: UITableView()) { (_, _, _) -> UITableViewCell? in
            print(selfWorkaround?.string)
            return nil
        }

        selfWorkaround = self
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,唯一的问题是,如果调用期间执行闭包super.init,则selfWorkaround闭包内将为零,您可能会得到意想不到的结果。(不过,在这种情况下,我认为不是 - 所以你应该安全地这样做。)

编辑:我们创建局部变量的原因weak是为了防止self对象被泄露。