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

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

Мне очень понравился целый раздел из руководства по использованию сильных, слабых и непризнанных в замыканиях:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

Примечание: я использовал термин закрытие вместо блока, который является более новым термином 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)
}

https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md

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

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

Посмотрите, предотвращает ли это сбой. (Это всего лишь предположение).

Другие вопросы по тегам