Как правильно управлять слабым я в быстрых блоках с аргументами
В моем TextViewTableViewCell
у меня есть переменная, чтобы отслеживать блок и метод configure, где блок передан и назначен.
Вот мой класс 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)
}
}
}
Когда я использую метод configure в моем методе 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]
?
Ответы
Ответ 1
Если self может быть nil в закрытии, используйте [слабый я].
Если self никогда не будет равным нулю в использовании закрытия, используйте [unowned self].
Если он сбой, когда вы используете [unowned self], я бы догадался, что в какой-то момент этого замыкания нет никого, поэтому вам нужно было пойти с [слабым я].
Мне очень понравился весь раздел из руководства по использованию strong, слабый и unowned в закрытии:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html
Примечание. Я использовал термин закрытие вместо block, который является новым термином Swift:
Разница между блоком (Objective C) и закрытием (Swift) в ios
Ответ 2
Поместите [unowned self]
до (text: String)...
в ваше закрытие. Это называется списком захвата и помещает инструкции владельца на символы, зафиксированные в закрытии.
Ответ 3
Используйте Список захвата
Определение списка захватов
Каждый элемент в списке захвата представляет собой спаривание слабых или незанятых ключевое слово со ссылкой на экземпляр класса (например, self) или переменная, инициализированная некоторым значением (например, delegate = self.delegate!). Эти пары записываются в пределах пары квадратов скобки, разделенные запятыми.
Поместите список захвата до списка параметров закрытия и возврата если они предоставлены:
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}
Если закрытие не указывает список параметров или возвращаемый тип, потому что они могут быть выведены из контекст, поместите список захвата в самом начале закрытия, за которым следует ключевое слово:
lazy var someClosure: Void -> String = {
[unowned self, weak delegate = self.delegate!] in
// closure body goes here
}
дополнительные объяснения
Ответ 4
** ИЗМЕНЕНО для Swift 4.2:
Как прокомментировал @Koen, swift 4.2 позволяет:
guard let self = self else {
return // Could not get a strong reference for self :'(
}
// Now self is a strong reference
self.doSomething()
PS: Так как у меня есть некоторые повышающие голоса, я хотел бы рекомендовать чтение об избежании закрытия.
РЕДАКТИРОВАНИЕ: Как прокомментировал @tim-vermeulen, Крис Латтнер сказал в пятницу 22 января 19:51:29 CST 2016, этот трюк не должен использоваться на себе, поэтому, пожалуйста, не используйте его. Проверьте информацию о не выходящих замыканиях и ответ списка захвата от @gbk. **
Для тех, кто использует [слабое я] в списке захвата, обратите внимание, что self может быть нулем, поэтому первое, что я делаю, это проверяю с помощью оператора guard
guard let 'self' = self else {
return
}
self.doSomething()
Если вам интересно, какие кавычки находятся вокруг self
это способ использовать себя внутри замыкания, не меняя имя на "слабый" или что-то еще.
Ответ 5
РЕДАКТИРОВАТЬ: Ссылка на обновленное решение от LightMan
Смотрите решение 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) }
Ответ 6
Swift 4.2
let closure = { [weak self] (_ parameter:Int) in
guard let self = self else { return }
self.method(parameter)
}
https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md
Ответ 7
Начиная с версии 4.2 🔸 мы можем сделать:
_ = { [weak self] value in
guard let self = self else { return }
print(self) //👈 will never be nil
}()
Ответ 8
Вы можете использовать [слабый self] или [unowned self] в списке захвата до ваших параметров блока. Список захвата - необязательный синтаксис.
[unowned self]
работает хорошо, потому что ячейка никогда не будет равна нулю. В противном случае вы можете использовать [weak self]
Ответ 9
Если вы терпите крах, вы, вероятно, нуждаетесь в [слабом я]
Я предполагаю, что создаваемый вами блок каким-то образом все еще подключен.
Создайте prepareForReuse и попробуйте очистить блок onTextViewEditClosure внутри этого.
func prepareForResuse() {
onTextViewEditClosure = nil
textView.delegate = nil
}
Смотрите, предотвращает ли это сбой. (Это просто догадка).