Как правильно справляться со слабым Я в стремительных блоках с помощью аргументов

129

В моем 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]?

  • 0
    Не могли бы вы выбрать ответ ниже в качестве правильного ответа тогда? Также обратите внимание, что с unowned вам не нужно укреплять себя в закрытии. Unowned лучше, чем слабый, потому что жизненный цикл вашей ячейки и контроллера представления связаны между собой.
  • 1
    Я понимаю, что [unowned self], если лучший вариант, но мое приложение падает, когда я его использую. Хотелось бы увидеть пример кода, использующего его, чтобы закрыть ответ.
Показать ещё 6 комментариев
Теги:
retain-cycle

9 ответов

173

Если 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

  • 7
    Apple назвала блоки «замыканиями» в своем самом первом документе для расширения языка Си. (Блоки или замыкания являются расширением C в самом начале. Только MM относится к Objective-C.) Даже я предпочитаю термин «замыкание», потому что «блоки» в C очень часто связаны с составными операторами, является неправильным в обоих языках, потому что это называется замыканием, даже если оно не закрывает объект (переменную или константу).
  • 1
    очень приятно ответил :)
Показать ещё 2 комментария
29

Поместите [unowned self] до (text: String)... в ваше закрытие. Это называется списком захвата и помещает инструкции владельца на символы, зафиксированные в закрытии.

  • 2
    Спасибо за наименование, я хотел это знать!
  • 3
    Я не думаю, что этот ответ полезен. [unowned self] потерпит крах, если self станет нулевым во время выполнения закрытия
Показать ещё 1 комментарий
23

Используйте Список захвата

Определение списка захватов

Каждый элемент в списке захвата представляет собой спаривание слабых или незанятых ключевое слово со ссылкой на экземпляр класса (например, 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
}

дополнительные объяснения

  • 2
    Вы использовали unowned для «self», что означает, что вы точно знаете, что «self» не будет нулевым, когда вы получите к нему доступ. Затем вы использовали force unwrap для «self.delegate» (что также означает, что вы точно знаете, что это не будет ноль), чтобы назначить его слабому var. Если вы точно знаете, что «self.delegate» не будет равным нулю, почему бы не использовать unowned на «делегате» вместо слабого?
22

РЕДАКТИРОВАНИЕ: Как прокомментировал @tim-vermeulen, Крис Латтнер сказал в пятницу 22 января 19:51:29 CST 2016, этот трюк не должен использоваться на себе, поэтому, пожалуйста, не используйте его. Проверьте информацию о не выходящих замыканиях и ответ списка захвата от @gbk.

Для тех, кто использует [слабое "я" в списке захвата, обратите внимание, что "я" может быть нулевым, поэтому первое, что я делаю, это проверяю с помощью выражения охранника

guard let 'self' = self else {
   return
}
self.doSomething()

Если вам интересно, какие кавычки находятся вокруг self, то вы можете использовать самость внутри замыкания, не меняя имя на "слабый" или что-то еще.

PS: Так как у меня есть некоторые повышающие голоса, я хотел бы рекомендовать чтение о не экранирующих закрытиях. TL; DR Большинство методов в конце вызывают закрытие, переданное аргументом, если это так, вы можете использовать @noescape в аргументе закрытия и можете неявно ссылаться на себя внутри замыкания.

  • 0
    Я предпочитаю этот ответ, какое изящное решение!
  • 2
    `self` - образец теневого копирования, статью о нем можно найти здесь arsenkin.com/swift-closure-without-ugly-strongSelf.html
Показать ещё 6 комментариев
17

РЕДАКТИРОВАТЬ: Удален трюк "Я"

Поскольку решение 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) }
  • 0
    Я обновил свой ответ, начиная со Swift 4.2 вы можете охранять let self = self ..
3

Вы можете использовать [слабый self] или [unowned self] в списке захвата до ваших параметров блока. Список захвата - необязательный синтаксис.

[unowned self] работает хорошо, потому что ячейка никогда не будет равна нулю. В противном случае вы можете использовать [weak self]

  • 0
    ячейка не является собственной, он не принадлежит к классу ячейки, он, вероятно, находится на контроллере представления ...
2

Используйте имя strongSelf, оно более явное, ИМО

_ = { [weak self] value in
    guard let strongSelf = self else { return }
    print(strongSelf) // will never be nil
}()
  • 0
    У других есть подобные решения, но «это» - C ++ IMHO. "strongSelf" - это соглашение Apple, и любой, кто взглянет на ваш код, будет знать, что происходит.
  • 1
    @ David H IMO фраза strongSelf явно объясняет переменные, означающие / побочный эффект, что хорошо, если код имеет более продолжительный характер. цените ваше мнение, хотя, не знал, что C ++ использовал такие фразы.
Показать ещё 1 комментарий
0

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

0

Если вы терпите крах, вы, вероятно, нуждаетесь в [слабом я]

Я предполагаю, что создаваемый вами блок каким-то образом все еще подключен.

Создайте prepareForReuse и попробуйте очистить блок onTextViewEditClosure внутри этого.

func prepareForResuse() {
   onTextViewEditClosure = nil
   textView.delegate = nil
}

Смотрите, предотвращает ли это сбой. (Это просто догадка).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню