Як правильно поводитись із слабким самоврядом у швидких блоках із аргументами


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

Коли я використовую метод налаштування у своєму 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]?


Чи можете ви вибрати правильну відповідь нижче? Також зауважте, що з невідомими вам не потрібно було б посилювати себе під час закриття. Тут невідоме краще, ніж слабке, оскільки життєвий цикл вашої комірки та контролера перегляду пов'язаний.
ikuramedia

1
Я розумію, що [невідомий я], якщо кращий варіант, але мій додаток виходить з ладу, коли я його використовую. Хочеться побачити зразок коду, використовуючи його, щоб закрити відповідь.
NatashaTheRobot

1
З документів: "Як і слабкі посилання, невідома посилання не тримає сильного положення щодо екземпляра, на який вона посилається. Однак, на відміну від слабкої посилання, однако, невідома посилання завжди має значення." Якщо ваш додаток виходить з ладу, це ймовірно, тому що невідомий застосовується до значення, яке є нульовим під час виконання.
Білл Паттерсон

Напевно, краще тут рекламувати заяву охоронця, ніж якщо дозволити прив’язку до strongSelf. Просто кажу, це як ідеальний кандидат :-D
Даніель Галаско

@NatashaTheRobot, що таке синтаксис [слабкий я] ?. виглядає як повідомлення, що передається в цілі C. Чи можете ви, будь ласка, додати трохи більше про синтаксис у питанні, будь ласка.
Виньєш

Відповіді:


178

Якщо самості може бути нульовим у закритті, використовуйте [слабке " .

Якщо самості ніколи не буде нульовим у закритті, скористайтеся [невідомим " .

Якщо вона виходить з ладу, коли ви використовуєте [невідомий я], я б здогадався, що "я" є нульовим в якийсь момент цього закриття, і саме тому вам довелося йти зі [слабким " натомість.

Мені дуже сподобався весь розділ з посібника про використання сильних , слабких та невідомих у закриттях:

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

Примітка: я використовував термін закриття замість блоку, який є новим терміном Swift:

Різниця між блоком (Завдання C) і закриттям (Swift) в ios


7
Apple назвала блоки "закриттями" в своєму першому документі для розширення мови С. (Блоки або закриття - це розширення C на перших порах. Тільки MM пов'язаний з Objective-C.) Навіть я вважаю за краще термін "закриття", тому що "блоки" в C дуже часто пов'язані зі складовими твердженнями, це є своєрідною помилкою в обох мовах, оскільки її називають закриттям, навіть не закриваючись над об’єктом (змінною чи постійною).
Амін Негм-Авад

1
дуже приємно відповів :)
iDevAmit

1
Я б запропонував ніколи не використовувати unowned. Не варто ризикувати причиною збоїв у вашій програмі.
Кайл Редфіарн

32

Поставте [unowned self]перед тим, як (text: String)...закрити. Це називається списком захоплення та розміщує інструкції щодо власності на символи, захоплені під час закриття.


2
Дякую, що назвали це, я хотів це знати!
rob5408

3
Я не думаю, що ця відповідь корисна. [невідомий Я] зазнає краху, якщо під час виконання закриття самоврядування стане нульовим
Юнус Недім Мехель,

3
немає абсолютно ніяких причин використовувати невідомі, крім (1) у надзвичайно незвичних ситуаціях, для виконання (це абсолютно не має значення тут і в 99,999% програмування) та (2) як питання дотримання стилів. Заява "Ви завжди повинні використовувати слабких, ніколи не відомих" є дуже розумною.
Fattie

29

** ВИДАЛЕНО для Swift 4.2:

Як прокоментував @Koen, швидкий 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. **

Для тих, хто використовує [слабке "у списку захоплення, зауважте, що самодоступність може бути нульовою, тому перше, що я роблю, - це перевірити це з заявою охорони

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

Якщо вам цікаво, що навколо лапок, це непростийself трюк, щоб використовувати себе всередині закриття, не потребуючи змінити ім'я на це , слабкий стиль чи інше.



2
Я схильний називати місцеве "Я" "strongSelf", щоб переконатися, що це не плутається з "за замовчуванням" і простіше визначити, чи охороняли ви сильну самовідвід.
Джастін Стенлі

1
Це не слід використовувати, оскільки це помилка компілятора: list.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/…
Тім Вермеулен

1
Я думаю, що коментар Кріса Леттнера у вищенаведеному посиланні полягає лише в тому, щоб не називати змінну як self(у задніх текстах ). Назвіть його чимось іншим, таким як nonOptionsSelf, і це буде добре.
OutOnAWeekend

1
На сьогоднішній день (swift 4.2) { [weak self] in guard let self = self else { return }можна використовувати без зворотних посилань
Koen.

26

Використовуйте список захоплення

Визначення списку захоплення

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

додаткові пояснення


3
Ви використовували невідомий "я", що означає, що ви точно знаєте, що "я" не буде нульовим, коли ви отримуєте доступ до нього. Тоді ви застосували силу для розгортання на "self.delegate" (що також означає, що ви точно знаєте, що це не буде нульовим), щоб призначити його слабкому var. Якщо ви точно знаєте, що "self.delegate" не буде нульовим, чому б не використовувати невідомий на "делегат" замість слабкого?
Roni Leshes

26

EDIT: Посилання на оновлене рішення 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
}()

Інші мають подібні рішення, але "це" є C ++ IMHO. "strongSelf" - це конвенція Apple, і кожен, хто погляне на ваш код, дізнається, що відбувається.
Девід Н

1
@ David H IMO ця фраза strongSelfчітко пояснює значення змінних / sideeffect, що добре, якщо код має більш тривалий характер. оцінив вашу думку, хоча не знав, що c ++ використовував таку фразу.
еоніст

3
Станом на Swift 4.2 ви можете guard let self = self else { return }розгортати [weak self]: github.com/apple/swift-evolution/blob/master/proposals/…
Амер Хукіч

@AmerHukic 👌.
еоніст


3

Ви можете використовувати [слабке самоврядування] або [невідомий Я] у списку захоплення до параметрів блоку. Список захоплення - необов'язковий синтаксис.

[unowned self]тут добре працює, тому що клітина ніколи не буде нульовою. Інакше можна використовувати[weak self]


1
осередок не самолюб, він не в класі клітин, він, мабуть, на контролері перегляду ...
Juan Boero

0

Якщо ви збиваєтеся, ніж вам, мабуть, потрібен [слабкий я]

Я здогадуюсь, що блок, який ви створюєте, якимось чином провіднаний.

Створіть файл ReadyForReuse і спробуйте очистити блок onTextViewEditClosure всередині цього.

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

Подивіться, чи це запобігає аварії. (Це просто здогад).


0

Закриття та потужні еталонні цикли [Про]

Як ви знаєте, закриття Swift може захопити екземпляр. Це означає, що ви можете використовувати selfвсередину закриття. Особливо escaping closure[About] може створити 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 - використовуйте його, коли ви впевнені, що термін служби власника екземпляра більший, ніж час закриття
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.