Jak poprawnie radzić sobie ze słabą jaźnią w szybkich blokach za pomocą argumentów

151

W moim TextViewTableViewCellmam zmienną do śledzenia bloku i metodę konfiguracji, w której blok jest przekazywany i przypisywany.
Oto moja TextViewTableViewCellklasa:

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

Kiedy używam metody konfiguracji w mojej cellForRowAtIndexPathmetodzie, jak prawidłowo używać słabego Ja w bloku, który przekazuję.
Oto, co mam bez słabego Ja:

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

UPDATE : Do pracy przy użyciu [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

Kiedy robię to [unowned self]zamiast [weak self]i wyciągam ifoświadczenie, aplikacja ulega awarii. Jakieś pomysły, jak to powinno działać [unowned self]?

NatashaTheRobot
źródło
Czy mógłbyś zatem wybrać odpowiedź poniżej jako poprawną? Zauważ również, że mając nieposiadane osoby, nie musisz wzmacniać siebie w swoim zamknięciu. Nieposiadanie jest tutaj lepsze niż słabe, ponieważ cykl życia komórki i kontrolera widoku są ze sobą powiązane.
ikuramedia
1
Zdaję sobie sprawę, że [osoba nieposiadająca] jest lepszą opcją, ale moja aplikacja ulega awarii, gdy jej używam. Chciałbym zobaczyć przykładowy kod wykorzystujący go do zamknięcia odpowiedzi.
NatashaTheRobot
1
Z dokumentacji: „Podobnie jak słabe referencje, referencje nieposiadające własności nie utrzymują silnej pozycji w instancji, do której się odnoszą. Jednak w przeciwieństwie do słabego odwołania zakłada się, że referencja nieposiadająca zawsze ma wartość.” Jeśli aplikacja ulega awarii, prawdopodobnie dlatego, że nieposiadanie jest stosowane do wartości zerowej w czasie wykonywania.
Bill Patterson
Prawdopodobnie lepiej jest ogłosić tutaj oświadczenie strażnika, niż gdyby pozwolić na powiązanie z strongSelf. Mówiąc tylko, to jest jak idealny kandydat :-D
Daniel Galasko
@NatashaTheRobot, Jaka jest składnia [słabe ja]? wygląda jak wiadomość przekazana w celu C. Czy możesz dodać więcej informacji na temat składni w pytaniu?
Vignesh,

Odpowiedzi:

178

Jeśli jaźń może być zerowa w zamknięciu, użyj [słabe ja] .

Jeśli jaźń nigdy nie będzie zerowa w zamknięciu, użyj [nieposiadanego ja] .

Jeśli to się zawiesza, kiedy używasz [nieposiadanego ja] , przypuszczam, że w pewnym momencie tego zamknięcia jaźń jest zerowa, dlatego zamiast tego musiałeś iść z [słabym ja] .

Bardzo podobał mi się cały rozdział podręcznika dotyczący używania mocnych , słabych i nieposiadanych w zamknięciach:

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

Uwaga: użyłem terminu zamknięcie zamiast bloku, który jest nowszym terminem Swift:

Różnica między blokiem (Cel C) a zamknięciem (Swift) w iOS

TenaciousJay
źródło
7
Apple nazwał bloki „zamknięciami” w swoim pierwszym dokumencie dotyczącym rozszerzenia języka C. (Bloki lub domknięcia są na początku rozszerzeniem C. Tylko MM jest powiązane z celem-C.) Nawet ja też wolę termin „zamknięcie”, ponieważ „bloki” w C są bardzo często związane z instrukcjami złożonymi, jest pewnym błędem w obu językach, ponieważ nazywa się to zamknięciem, nawet jeśli nie zamyka obiektu (zmiennego lub stałego).
Amin Negm-Awad
1
bardzo ładnie odpowiedział :)
iDevAmit
1
Sugerowałbym nigdy nie używać unowned. Nie warto ryzykować awarii aplikacji.
Kyle Redfearn,
32

Umieść [unowned self]przed (text: String)...zamknięciem. Nazywa się to listą przechwytywania i umieszcza instrukcje dotyczące własności na symbolach przechwyconych w zamknięciu.

ikuramedia
źródło
2
Dzięki za nazwanie tego, chciałem to wiedzieć!
rob5408
3
Nie sądzę, aby ta odpowiedź była przydatna. [osoba nieposiadająca] ulegnie awarii, jeśli wartość self stanie się zerowa podczas wykonywania zamknięcia
Yunus Nedim Mehel
3
nie ma absolutnie żadnego powodu, aby używać nieposiadania własności, poza (1) w skrajnie nietypowych sytuacjach, dla wydajności (jest to zupełnie nieistotne tutaj i w 99,999% programowania) i (2) jako kwestia egzekwowania stylu. Stwierdzenie „Zawsze powinieneś używać słabych, nigdy nieposiadanych” jest bardzo rozsądne.
Fattie
29

** EDYTOWANE dla Swift 4.2:

Jak skomentował @Koen, Swift 4.2 umożliwia:

guard let self = self else {
   return // Could not get a strong reference for self :`(
}

// Now self is a strong reference
self.doSomething()

PS: Ponieważ mam kilka pozytywnych głosów, chciałbym polecić lekturę o unikaniu zamknięć .

ZMIENIONO: Jak skomentował @ tim-vermeulen, Chris Lattner powiedział w piątek, 22 stycznia, 19:51:29 CST 2016, ta sztuczka nie powinna być używana na sobie, więc proszę jej nie używać. Sprawdź informacje o zamknięciach bez ucieczki i odpowiedź na listę przechwytywania z @gbk. **

Dla tych, którzy używają [słabe ja] na liście przechwytywania, zauważ, że self może być zerowe, więc pierwszą rzeczą, którą robię, jest sprawdzenie tego za pomocą oświadczenia strażnika

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

Jeśli zastanawiasz się, czym są cudzysłowy, selfto profesjonalna sztuczka polegająca na użyciu self w zamknięciu bez konieczności zmiany nazwy na to , słabySelf lub cokolwiek innego.

LightMan
źródło
2
`self 'to próbka cieniowania, artykuł na ten temat można znaleźć tutaj arsenkin.com/swift-closure-without-ugly-strongSelf.html
Cullen
2
Zwykle nazywam lokalne „ja” „silnym sobą”, aby upewnić się, że nie jest ono mylone z domyślnym ja i łatwiej jest zauważyć, czy strzegłeś silnego odniesienia do siebie.
Justin Stanley,
1
Nie należy tego używać, ponieważ jest to błąd kompilatora: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/ ...
Tim Vermeulen
1
Myślę, że komentarz Chrisa Lattnera w powyższym linku dotyczy po prostu nienazywania zmiennej jako self(w odwrotnych znakach). Nazwij to czymś innym, na przykład nonOptionalSelf i wszystko będzie dobrze.
OutOnAWeekend
1
W dzisiejszych czasach (swift 4.2) { [weak self] in guard let self = self else { return }można używać bez odwrotnych znaków
Koen.
26

Użyj listy przechwytywania

Definiowanie listy przechwytywania

Każda pozycja na liście przechwytywania jest parą słowa kluczowego o słabym lub nieposiadanym imieniu z odniesieniem do instancji klasy (takiej jak self) lub zmiennej zainicjowanej pewną wartością (na przykład delegate = self.delegate!). Te pary są zapisywane w nawiasach kwadratowych oddzielonych przecinkami.

Umieść listę przechwytywania przed listą parametrów zamknięcia i typem zwracania, jeśli są podane:

lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // closure body goes here 
} 

Jeśli zamknięcie nie określa listy parametrów ani typu zwracanego, ponieważ można je wywnioskować z kontekstu, należy umieścić listę przechwytywania na samym początku zamknięcia, a następnie słowo kluczowe in:

lazy var someClosure: Void -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // closure body goes here
}

dodatkowe wyjaśnienia

gbk
źródło
3
Użyłeś nieposiadania dla „siebie”, co oznacza, że ​​na pewno wiesz, że „ja” nie będzie zerowe, kiedy uzyskasz do niego dostęp. Następnie użyłeś wymuszonego rozpakowania na "self.delegate" (co również oznacza, że ​​wiesz na pewno, że nie będzie zero), aby przypisać go do słabej zmiennej var. Jeśli wiesz na pewno, że „self.delegate” nie będzie zerowe, dlaczego nie użyć unellow na „delegacie” zamiast słabe?
Roni Leshes
26

EDYCJA: odniesienie do zaktualizowanego rozwiązania firmy LightMan

Zobacz rozwiązanie LightMan . Do tej pory korzystałem z:

input.action = { [weak self] value in
    guard let this = self else { return }
    this.someCall(value) // 'this' isn't nil
}

Lub:

input.action = { [weak self] value in
    self?.someCall(value) // call is done if self isn't nil
}

Zwykle nie trzeba określać typu parametru, jeśli jest on wywnioskowany.

Możesz całkowicie pominąć parametr, jeśli go nie ma lub jeśli odnosisz się do niego jak $0w zamknięciu:

input.action = { [weak self] in
    self?.someCall($0) // call is done if self isn't nil
}

Tylko dla kompletności; jeśli przekazujesz zamknięcie do funkcji, a parametr nie jest @escaping, nie potrzebujesz weak self:

[1,2,3,4,5].forEach { self.someCall($0) }
Ferran Maylinch
źródło
9

Od Swifta 4,2 możemy:

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()
eonista
źródło
Inni mają podobne rozwiązania, ale „to” to C ++ IMHO. „StrongSelf” to konwencja firmy Apple i każdy, kto spojrzy na Twój kod, będzie wiedział, co się dzieje.
David H,
1
@ David H IMO fraza strongSelfwyraźnie wyjaśnia znaczenie zmiennych / efekt uboczny, co jest miłe, jeśli kod jest dłuższy. jednak doceniam twoją opinię, nie wiedziałem, że c ++ używa takich fraz.
eonista
3
Począwszy od Swift 4.2, możesz użyć guard let self = self else { return }do rozpakowania [weak self]: github.com/apple/swift-evolution/blob/master/propiments/…
Amer Hukic
@AmerHukic 👌.
eonista
3

Możesz użyć [słabe ja] lub [nieposiadane własne] na liście przechwytywania przed parametrami bloku. Lista przechwytywania ma opcjonalną składnię.

[unowned self]działa dobrze, ponieważ komórka nigdy nie będzie zerowa. W przeciwnym razie możesz użyć[weak self]

Rufus
źródło
1
komórka nie jest samo, nie jest od klasy komórek, jest on prawdopodobnie na viewcontroller ...
Juan Boero
0

Jeśli masz awarię, niż prawdopodobnie potrzebujesz [słabe ja]

Domyślam się, że blok, który tworzysz, jest nadal w jakiś sposób podłączony.

Utwórz przygotowanieForReuse i spróbuj wyczyścić znajdujący się w nim blok onTextViewEditClosure.

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

Sprawdź, czy to zapobiega awarii. (To tylko przypuszczenie).

Michael Grey
źródło
0

Zamknięcie i silne cykle referencyjne [Informacje]

Jak wiesz, zamknięcie Swifta może uchwycić instancję. Oznacza to, że możesz używać selfwewnątrz zapięcia. Szczególnie escaping closure[About] może utworzyć strong reference cyclektóry. Nawiasem mówiąc, musisz jawnie używać w selfśrodku escaping closure.

Szybkie zamknięcie ma Capture Listfunkcję, która pozwala uniknąć takiej sytuacji i przerwać cykl odniesienia, ponieważ nie ma silnego odniesienia do przechwyconej instancji. Element Capture List jest parą weak/ unownedi odniesieniem do klasy lub zmiennej.

Na przykład

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- bardziej preferowane, używaj go, gdy jest to możliwe
  • unowned - używaj go, gdy masz pewność, że czas życia właściciela instancji jest dłuższy niż zamknięcie
yoAlex5
źródło