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

151

По моему 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)
        }
    }
}

Когда я использую метод 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]?

NatashaTheRobot
источник
Не могли бы вы выбрать ответ ниже в качестве правильного ответа тогда? Также обратите внимание, что с unowned вам не нужно укреплять себя в закрытии. Unowned лучше, чем слабый, потому что жизненный цикл вашей ячейки и контроллера представления связаны.
ikuramedia
1
Я понимаю, что [unowned self] - лучший вариант, но мое приложение вылетает, когда я его использую. Хотелось бы увидеть пример кода, использующего его, чтобы закрыть ответ.
NatashaTheRobot
1
Из документов: «Как и слабые ссылки, неизвестная ссылка не удерживает сильную привязку к экземпляру, на который она ссылается. Однако, в отличие от слабой ссылки, предполагается, что неизвестная ссылка всегда имеет значение.» скорее всего, потому что unowned применяется к значению, которое равно нулю во время выполнения.
Билл Паттерсон
Вероятно, лучше рекламировать здесь защитную инструкцию, чем если бы она была привязана к strongSelf. Просто
скажу
@NatashaTheRobot, какой синтаксис [слабое я]? выглядит как сообщение, передаваемое в цель C. Не могли бы вы добавить немного больше о синтаксисе в вопросе, пожалуйста.
Виньеш

Ответы:

178

Если « я» может быть нулевым в замыкании, используйте « слабое я» .

Если self никогда не будет нулевым в закрытии, используйте [unowned self] .

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

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

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

Примечание: я использовал термин закрытие вместо блока, который является более новым термином Swift:

Разница между блоком (цель C) и закрытием (Swift) в IOS

TenaciousJay
источник
7
Apple назвала блоки «замыканиями» в своем самом первом документе для расширения языка Си. (Блоки или замыкания являются расширением C в самом начале. Только MM относится к Objective-C.) Даже я предпочитаю термин «замыкание» тоже, потому что «блоки» в C очень часто связаны с составными операторами. является неправильным в обоих языках, потому что это называется замыканием, даже если оно не закрывается над объектом (переменным или постоянным).
Амин Негм-Авад
1
очень приятно ответил :)
iDevAmit
1
Я бы предложил никогда не использовать unowned. Не стоит рисковать сбоем вашего приложения.
Кайл Редфирн
32

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

ikuramedia
источник
2
Спасибо, что назвали это, я хотел это знать!
rob5408
3
Я не думаю, что этот ответ полезен. [Неизвестное Я]
потерпит
3
нет абсолютно никакой причины использовать неизвестное, кроме (1) в чрезвычайно необычных ситуациях, для производительности (это совершенно не имеет значения здесь и в 99,999% программирования) и (2) для обеспечения соблюдения стилей. Утверждение «Вы должны всегда использовать слабых, никогда не принадлежащих» очень разумно.
Толстяк
29

** ИЗМЕНЕНО для 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- это профессиональный трюк для использования себя внутри замыкания без необходимости менять имя на this , weakSelf или что-то еще.

Светлый человек
источник
2
` self`
Cullen SUN
2
Я склонен называть локальное «я» «strongSelf», чтобы убедиться, что он не перепутан с «Я» по умолчанию и его легче обнаружить, если вы защищены для сильной ссылки на себя.
Джастин Стэнли
1
Это не должно использоваться, так как это ошибка компилятора: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/…
Тим Вермёлен
1
Я думаю, что комментарий Криса Латтнера в приведенной выше ссылке почти не называет переменную как self(в обратных галочках). Назовите его как-нибудь еще, например nonOptionalSelf, и все будет хорошо.
OutOnAWeekend
1
В настоящее время (swift 4.2) { [weak self] in guard let self = self else { return }может использоваться без обратных галочек, и фактически поддерживается: github.com/apple/swift-evolution/blob/master/proposals/…
Koen.
26

Использовать список захвата

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

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

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

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

РЕДАКТИРОВАТЬ: Ссылка на обновленное решение от 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) }
Ферран Майлинч
источник
9

Начиная с версии 4.2 🔸 мы можем сделать:

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()
eonist
источник
У других есть похожие решения, но «это» - C ++ IMHO. "strongSelf" - это соглашение Apple, и любой, кто взглянет на ваш код, будет знать, что происходит.
Дэвид Х
1
@ David H IMO фраза strongSelfявно объясняет переменные, означающие / побочный эффект, что хорошо, если код имеет более продолжительную природу. цените ваше мнение, хотя, не знал, что C ++ использовал такие фразы.
eonist
3
Начиная с Swift 4.2 вы можете использовать его guard let self = self else { return }для распаковки [weak self]: github.com/apple/swift-evolution/blob/master/proposals/…
Amer Hukic
@AmerHukic 👌.
эонист
3

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

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

Руфус
источник
1
клетка не является собой, он не принадлежит классу ячейки, он, вероятно, находится на контроллере представления ...
Хуан Боэро
0

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

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

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

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

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

Майкл Грей
источник
0

Закрытие и сильные опорные циклы [О программе]

Как вы знаете, закрытие Swift может захватить экземпляр. Это означает, что вы можете использовать selfвнутри замыкания. Особенно escaping closure[О] может создать strong reference cycleкоторый. Кстати, вы должны явно использовать selfвнутри escaping closure.

Быстрое закрытие имеет 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 - используйте его, когда вы уверены, что время жизни экземпляра владельца больше, чем замыкание
yoAlex5
источник