Как правильно справляться со слабым Я в стремительных блоках с помощью аргументов
В моем TextViewTableViewCell
У меня есть переменная для отслеживания блока и метод конфигурации, в котором блок передается и назначается.
Вот мой TextViewTableViewCell
учебный класс:
//
// TextViewTableViewCell.swift
//
import UIKit
class TextViewTableViewCell: UITableViewCell, UITextViewDelegate {
@IBOutlet var textView : UITextView
var onTextViewEditClosure : ((text : String) -> Void)?
func configure(#text: String?, onTextEdit : ((text : String) -> Void)) {
onTextViewEditClosure = onTextEdit
textView.delegate = self
textView.text = text
}
// #pragma mark - Text View Delegate
func textViewDidEndEditing(textView: UITextView!) {
if onTextViewEditClosure {
onTextViewEditClosure!(text: textView.text)
}
}
}
Когда я использую метод конфигурации в моем cellForRowAtIndexPath
метод, как правильно использовать слабое Я в блоке, который я передаю.
Вот что я имею без слабой самости:
let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {(text: String) in
// THIS SELF NEEDS TO BE WEAK
self.body = text
})
cell = bodyCell
ОБНОВЛЕНИЕ: я получил следующее, чтобы работать с помощью [weak self]
:
let myCell = tableView.dequeueReusableCellWithIdentifier(textViewCellIdenfitier) as TextViewTableViewCell
myCell.configure(text: body, onTextEdit: {[weak self] (text: String) in
if let strongSelf = self {
strongSelf.body = text
}
})
cell = myCell
Когда я делаю [unowned self]
вместо [weak self]
и вынуть if
Заявление, приложение вылетает. Любые идеи о том, как это должно работать с [unowned self]
?
11 ответов
Если " я" может быть нулевым в замыкании, используйте " слабое я".
Если self никогда не будет нулевым в закрытии, используйте [unowned self].
Если происходит сбой, когда вы используете [unowned self], я бы предположил, что self - это ноль в какой-то момент в этом закрытии, поэтому вместо этого вам пришлось использовать [слабое self].
Мне очень понравился целый раздел из руководства по использованию сильных, слабых и непризнанных в замыканиях:
Примечание: я использовал термин закрытие вместо блока, который является более новым термином Swift:
Разница между блоком (Objective C) и замыканием (Swift) в IOS
РЕДАКТИРОВАНИЕ: Как прокомментировал @tim-vermeulen, Крис Латтнер сказал в пятницу 22 января 19:51:29 CST 2016, этот трюк не должен использоваться на себе, поэтому, пожалуйста, не используйте его. Проверьте информацию о не выходящих замыканиях и ответ списка захвата от @gbk.
Для тех, кто использует [слабое я] в списке захвата, обратите внимание, что self может быть нулем, поэтому первое, что я делаю, это проверяю с помощью оператора guard
guard let `self` = self else {
return
}
self.doSomething()
Если вам интересно, что вокруг кавычек self
это трюк для использования себя внутри замыкания без необходимости менять имя на this, weakSelf или что-то еще.
PS: Так как у меня есть некоторые повышающие голоса, я хотел бы рекомендовать чтение о не экранирующих закрытиях. TL;DR Большинство методов в конце вызывают закрытие, переданное аргументом, если это так, вы можете использовать @noescape в аргументе закрытия и можете неявно ссылаться на себя внутри замыкания.
РЕДАКТИРОВАТЬ: Удален трюк "Я"
Поскольку решение LightMan не рекомендуется, я обычно делаю:
input.action = { [weak self] value in
guard let this = self else { return }
this.someCall(value) // 'this' isn't nil
}
Или же:
input.action = { [weak self] value in
self?.someCall(value) // call is done if self isn't nil
}
Обычно вам не нужно указывать тип параметра, если он выводится.
Вы можете вообще опустить параметр, если его нет, или если вы ссылаетесь на него как $0
в закрытии:
input.action = { [weak self] in
self?.someCall($0) // call is done if self isn't nil
}
Просто для полноты картины; если вы передаете замыкание функции, а параметр не @escaping
вам не нужно weak self
:
[1,2,3,4,5].forEach { self.someCall($0) }
Положил [unowned self]
до (text: String)...
в вашем закрытии. Это называется списком захвата и помещает инструкции по владению символами, захваченными в закрытии.
Использовать список захвата
Определение списка захвата
Каждый элемент в списке захвата представляет собой пару слабого или неизвестного ключевого слова со ссылкой на экземпляр класса (например, на себя) или на переменную, инициализированную некоторым значением (например, делегат = self.delegate!). Эти пары пишутся в виде пары квадратных скобок, разделенных запятыми.
Поместите список захвата перед списком параметров замыкания и типом возврата, если они предоставлены:
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
Если в замыкании не указан список параметров или тип возвращаемых данных, поскольку они могут быть выведены из контекста, поместите список захвата в самое начало замыкания, а затем введите ключевое слово in:
lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
Используйте имя strongSelf, это более явно IMO
_ = { [weak self] value in
guard let strongSelf = self else { return }
print(strongSelf) // will never be nil
}()
Swift 4.2
let closure = { [weak self] (_ parameter:Int) in
guard let self = self else { return }
self.method(parameter)
}
Вы можете использовать [Слабое Я] или [Неизвестное Я] в списке захвата до ваших параметров блока. Список захвата является необязательным синтаксисом.
[unowned self]
Здесь хорошо работает, потому что клетка никогда не будет равна нулю. В противном случае вы можете использовать [weak self]
Из
Swift 5.3
, вам не нужно разворачивать
self
в закрытии, если вы пройдете
[self]
перед
in
в закрытии.
Ссылаться
someFunctionWithEscapingClosure { [self] in x = 100 }
в этом быстром документе
Циклы закрытия и сильных ссылок [О программе]
Как вы знаете, закрытие Swift может захватить экземпляр. Это означает, что вы можете использоватьself
внутри укупорки. Особенноescaping closure
[About] может создатьstrong reference cycle
который. Кстати, вы должны явно использоватьself
внутри escaping closure
.
Быстрое закрытие Capture List
функция, которая позволяет избежать такой ситуации и разорвать цикл ссылок, потому что не имеет сильной ссылки на захваченный экземпляр. Элемент Capture List представляет собой паруweak
/unowned
и ссылка на класс или переменную.
Например
class A {
private var completionHandler: (() -> Void)!
private var completionHandler2: ((String) -> Bool)!
func nonescapingClosure(completionHandler: () -> Void) {
print("Hello World")
}
func escapingClosure(completionHandler: @escaping () -> Void) {
self.completionHandler = completionHandler
}
func escapingClosureWithPArameter(completionHandler: @escaping (String) -> Bool) {
self.completionHandler2 = completionHandler
}
}
class B {
var variable = "Var"
func foo() {
let a = A()
//nonescapingClosure
a.nonescapingClosure {
variable = "nonescapingClosure"
}
//escapingClosure
//strong reference cycle
a.escapingClosure {
self.variable = "escapingClosure"
}
//Capture List - [weak self]
a.escapingClosure {[weak self] in
self?.variable = "escapingClosure"
}
//Capture List - [unowned self]
a.escapingClosure {[unowned self] in
self.variable = "escapingClosure"
}
//escapingClosureWithPArameter
a.escapingClosureWithPArameter { [weak self] (str) -> Bool in
self?.variable = "escapingClosureWithPArameter"
return true
}
}
}
weak
- предпочтительнее, используйте по возможностиunowned
- используйте его, когда уверены, что время жизни владельца экземпляра больше, чем закрытие
Если вы терпите крах, чем вам, вероятно, нужно [слабое я]
Я предполагаю, что блок, который вы создаете, каким-то образом все еще подключен.
Создайте prepareForReuse и попробуйте очистить блок onTextViewEditClosure внутри него.
func prepareForResuse() {
onTextViewEditClosure = nil
textView.delegate = nil
}
Посмотрите, предотвращает ли это сбой. (Это всего лишь предположение).