сам захвачен закрытием до того, как все члены были инициализированы, но я их инициализировал

Это игрушечный пример, но он точно уменьшает мою ситуацию:

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
        }
    }
}

Я пытаюсь сделать свой источник данных табличного представления автономным, и мой способ сделать это (пока) - создать подкласс UITableViewDiffableDataSource. Это отлично работает, за исключением случаев, когда я пытаюсь предоставить своему подклассу настраиваемый инициализатор. На примере игрушки показана проблема.

То, как я хочу заполнить ячейку, полностью зависит от значения, которое может измениться позже в жизни источника данных. Поэтому его нельзя жестко встроить в функцию провайдера сотовой связи. Я не могу сослаться здесь просто наstring, значение, которое было передано в инициализаторе; Я должен сослаться наself.string потому что другой код будет иметь возможность изменить этот источник данных string instance свойство позже, и я хочу, чтобы провайдер ячейки использовал это новое значение, когда это произойдет.

Тем не менее, я получаю сообщение об ошибке "сам захвачен закрытием до инициализации всех членов". Это кажется несправедливым. Я сделал инициализировать мойstring свойство экземпляра перед вызовом super.init. Таким образом, он имеет значение в самый ранний момент, когда может быть вызван метод поставщика ячейки.

3 ответа

Решение

Хотя я не совсем уверен, почему 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
    }
}

Однако единственная проблема заключается в том, что если закрытие выполняется во время вызоваsuper.init, тогда selfWorkaroundбудет равно нулю внутри закрытия, и вы можете получить неожиданные результаты. (В данном случае, однако, я не думаю, что это так - так что вы должны быть в безопасности.)

Изменить: причина, по которой мы делаем локальную переменную weak чтобы предотвратить self объект от утечки.

Вы можете получить доступ к себе через tableView.datasource, и он решит большую часть проблемы.

Расширяя ответ Абхираджа Кумара от 16 февраля 2020 года, вот пример использования TableView, предоставленного для "обратной связи", чтобы получить источник данных, который вы прикрепили к таблице... то есть "себя":

class MyDataSource: UITableViewDiffableDataSource<String,String> {
    var string : String?
    init(string:String?) {
        self.string = string
        super.init(tableView: UITableView()) { (tableView, _, _) -> UITableViewCell? in
            
            // Very sketchy reach-through to get "self", forced by API design where
            // super.init() requires closure as a parameter
            let hack_self = tableView.dataSource! as! MyDataSource
            
            let selfDotStr = hack_self.string
            
            print("In closure, self.string is \(selfDotStr)")
            
            return nil // would return a real cell here in real application
        }
    }
}
Другие вопросы по тегам