Wie man mit schwachen Selbst in schnellen Blöcken mit Argumenten richtig umgeht


151

In meinem habe TextViewTableViewCellich eine Variable, um einen Block zu verfolgen, und eine Konfigurationsmethode, bei der der Block übergeben und zugewiesen wird.
Hier ist meine TextViewTableViewCellKlasse:

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

Wenn ich die configure-Methode in meiner cellForRowAtIndexPathMethode verwende, wie verwende ich das schwache Selbst in dem Block, den ich übergebe, richtig?
Folgendes habe ich ohne das schwache Selbst:

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 : Ich habe Folgendes zum Arbeiten [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

Wenn ich die Anweisung [unowned self]anstelle von [weak self]und ifherausnehme, stürzt die App ab. Irgendwelche Ideen, wie das funktionieren soll [unowned self]?


Könnten Sie dann eine Antwort unten als die richtige Antwort auswählen? Beachten Sie auch, dass Sie mit nicht besessenen Personen sich nicht innerhalb Ihrer Schließung stärken müssen. Nicht besessen ist hier besser als schwach, da der Lebenszyklus Ihrer Zelle und des View Controllers miteinander verknüpft sind.
Ikuramedia

1
Mir ist klar, dass [nicht besessenes Selbst] die bessere Option ist, aber meine App stürzt ab, wenn ich sie benutze. Würde gerne ein Codebeispiel sehen, das es verwendet, um die Antwort zu schließen.
NatashaTheRobot

1
Aus den Dokumenten: "Wie schwache Referenzen behält eine nicht besessene Referenz die Instanz, auf die sie verweist, nicht fest. Im Gegensatz zu einer schwachen Referenz wird jedoch angenommen, dass eine nicht besessene Referenz immer einen Wert hat." Wenn Ihre App abstürzt, ist dies der Fall wahrscheinlich, weil nicht besessen auf einen Wert angewendet wird, der zur Laufzeit gleich Null ist.
Bill Patterson

Wahrscheinlich ist es besser, hier eine Wachaussage zu bewerben, als sich an strongSelf binden zu lassen. Nur zu sagen, das ist wie der perfekte Kandidat :-D
Daniel Galasko

@ NatashaTheRobot, Welche Syntax ist [schwaches Selbst]? sieht aus wie eine Nachricht, die in Ziel C übergeben wird. Können Sie bitte etwas mehr über die Syntax in der Frage hinzufügen?
Vignesh

Antworten:


178

Wenn das Selbst im Verschluss gleich Null sein könnte, benutze [schwaches Selbst] .

Wenn das Selbst im Verschluss niemals Null sein wird, verwenden Sie [nicht besessenes Selbst] .

Wenn es abstürzt, wenn Sie [nicht besessenes Selbst] verwenden, würde ich vermuten, dass das Selbst irgendwann in diesem Abschluss gleich Null ist, weshalb Sie stattdessen mit [schwachem Selbst] gehen mussten .

Ich mochte den gesamten Abschnitt aus dem Handbuch über die Verwendung von starken , schwachen und nicht besessenen Verschlüssen sehr:

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

Hinweis: Ich habe den Begriff Closure anstelle von Block verwendet, der der neuere Swift-Begriff ist:

Unterschied zwischen Block (Ziel C) und Abschluss (Swift) in ios


7
Apple nannte Blöcke in seinem ersten Dokument für eine C-Spracherweiterung "Schließungen". (Blöcke oder Verschlüsse sind von Anfang an eine Erweiterung von C. Nur MM ist mit Objective-C verwandt.) Auch ich bevorzuge den Begriff "Verschluss", da "Blöcke" in C sehr oft mit zusammengesetzten Aussagen zusammenhängen ist in beiden Sprachen eine Art Fehler, da es als Abschluss bezeichnet wird, auch wenn es nicht über einem Objekt (variabel oder konstant) geschlossen wird.
Amin Negm-Awad

1
sehr schön beantwortet :)
iDevAmit

1
Ich würde vorschlagen, nie zu verwenden unowned. Das Risiko eines Absturzes Ihrer App ist es nicht wert.
Kyle Redfearn

32

Setzen Sie [unowned self]vorher (text: String)...in Ihren Verschluss. Dies wird als Erfassungsliste bezeichnet und platziert Besitzanweisungen für Symbole, die im Abschluss erfasst wurden.


2
Danke, dass du es benannt hast, das wollte ich wissen!
rob5408

3
Ich halte diese Antwort nicht für nützlich. [nicht besessenes Selbst] wird abstürzen, wenn Selbst während der Ausführung der Schließung Null wird
Yunus Nedim Mehel

3
Es gibt absolut keinen Grund , Unbesitz, außer (1) in äußerst ungewöhnlichen Situationen, für die Leistung (dies ist hier und in 99,999% der Programmierung völlig irrelevant) und (2) als Angelegenheit zur Durchsetzung des Stils zu verwenden. Die Aussage "Sie sollten immer schwach, nie unbesessen verwenden" ist sehr vernünftig.
Fattie

29

** BEARBEITET für Swift 4.2:

Wie @Koen kommentierte, erlaubt 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: Da ich einige Up-Votes habe, möchte ich die Lektüre über die Flucht vor Schließungen empfehlen .

BEARBEITET: Wie @ tim-vermeulen kommentiert hat, sagte Chris Lattner am Fri Jan 22 19:51:29 CST 2016, dieser Trick sollte nicht für sich selbst verwendet werden, also bitte nicht verwenden. Überprüfen Sie die Informationen zu nicht entkommenden Schließungen und die Antwort auf die Erfassungsliste von @gbk. **

Beachten Sie für diejenigen, die [schwaches Selbst] in der Erfassungsliste verwenden, dass Selbst Null sein kann. Als erstes überprüfe ich dies mit einer Guard-Anweisung

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

Wenn Sie sich fragen, um welche Anführungszeichen es sich handelt, selfist dies ein Pro-Trick, um sich selbst innerhalb des Verschlusses zu verwenden, ohne den Namen in dies , schwaches Selbst oder was auch immer ändern zu müssen .


2
`self` ist ein Beispiel für Schatten, ein Artikel darüber finden Sie hier arsenkin.com/swift-closure-without-ugly-strongSelf.html
Cullen SUN

2
Ich neige dazu, das lokale "Selbst" "starkes Selbst" zu nennen, um sicherzustellen, dass es nicht mit dem Standard-Selbst verwechselt wird und leichter zu erkennen ist, ob Sie auf eine starke Selbstreferenz geachtet haben.
Justin Stanley

1
Dies sollte nicht verwendet werden, da es sich um einen Compiler-Fehler handelt: lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160118/…
Tim Vermeulen

1
Ich denke, in dem Kommentar von Chris Lattner im obigen Link geht es nur darum, die Variable nicht als self(in Backticks) zu benennen. Nennen Sie es etwas anderes wie nonOptionalSelf und es wird in Ordnung sein.
OutOnAWeekend

1
Heutzutage (swift 4.2) { [weak self] in guard let self = self else { return }kann ohne Backticks verwendet werden und wird tatsächlich unterstützt: github.com/apple/swift-evolution/blob/master/proposals/…
Koen.

26

Verwenden Sie Capture - Liste

Erfassen einer Erfassungsliste

Jedes Element in einer Erfassungsliste ist eine Paarung des schwachen oder nicht besessenen Schlüsselworts mit einem Verweis auf eine Klasseninstanz (z. B. self) oder eine Variable, die mit einem bestimmten Wert initialisiert wurde (z. B. delegate = self.delegate!). Diese Paarungen werden in zwei eckigen Klammern geschrieben, die durch Kommas getrennt sind.

Platzieren Sie die Erfassungsliste vor der Parameterliste eines Abschlusses und geben Sie den Rückgabetyp zurück, falls diese bereitgestellt werden:

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

Wenn ein Abschluss keine Parameterliste oder keinen Rückgabetyp angibt, da diese aus dem Kontext abgeleitet werden können, platzieren Sie die Erfassungsliste ganz am Anfang des Abschlusses, gefolgt vom Schlüsselwort in:

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

zusätzliche Erklärungen


3
Sie haben unbesessen für "Selbst" verwendet, was bedeutet, dass Sie sicher wissen, dass "Selbst" nicht gleich Null ist, wenn Sie darauf zugreifen. Dann haben Sie "self.delegate" erzwungen, um es einem schwachen var zuzuweisen (was auch bedeutet, dass Sie sicher sind, dass es nicht null ist). Wenn Sie sicher sind, dass "self.delegate" nicht gleich Null ist, warum nicht "nicht besessen" für "delegate" anstelle von "schwach" verwenden?
Roni Leshes

26

BEARBEITEN: Verweis auf eine aktualisierte Lösung von LightMan

Siehe LightMans Lösung . Bis jetzt habe ich verwendet:

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

Oder:

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

Normalerweise müssen Sie den Parametertyp nicht angeben, wenn er abgeleitet wird.

Sie können den Parameter ganz weglassen, wenn es keinen gibt oder wenn Sie sich wie $0im Abschluss darauf beziehen :

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

Nur der Vollständigkeit halber; Wenn Sie den Abschluss an eine Funktion übergeben und der Parameter nicht @escaping, benötigen Sie kein weak self:

[1,2,3,4,5].forEach { self.someCall($0) }

9

Ab Swift 4.2 🔸 können wir:

_ = { [weak self] value in
    guard let self = self else { return }
    print(self) //👈 will never be nil
}()

Andere haben ähnliche Lösungen, aber "das" ist C ++ IMHO. "strongSelf" ist eine Apple-Konvention und jeder, der einen Blick auf Ihren Code wirft, weiß, was los ist.
David H

1
@ David H IMO der Ausdruck strongSelferklärt explizit die Variablen Bedeutung / Nebeneffekt, was schön ist, wenn der Code länger ist. Ich schätze Ihre Meinung, wusste nicht, dass C ++ eine solche Formulierung verwendet.
Eonist

3
Ab Swift 4.2 können Sie guard let self = self else { return }Folgendes auspacken [weak self]: github.com/apple/swift-evolution/blob/master/proposals/…
Amer Hukic

@AmerHukic 👌.
Eonist


3

Sie können [schwaches Selbst] oder [nicht besessenes Selbst] in der Erfassungsliste vor Ihren Parametern des Blocks verwenden. Die Erfassungsliste ist eine optionale Syntax.

[unowned self]funktioniert hier gut, weil die Zelle niemals Null sein wird. Ansonsten können Sie verwenden[weak self]


1
Die Zelle ist nicht selbst, er ist nicht in der Zellklasse, er ist wahrscheinlich auf einem Viewcontroller ...
Juan Boero

0

Wenn Sie abstürzen, brauchen Sie wahrscheinlich [schwaches Selbst]

Ich vermute, dass der Block, den Sie erstellen, irgendwie immer noch verkabelt ist.

Erstellen Sie eine prepareForReuse und versuchen Sie, den darin enthaltenen onTextViewEditClosure-Block zu löschen.

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

Überprüfen Sie, ob dies den Absturz verhindert. (Es ist nur eine Vermutung).


0

Abschluss und starke Referenzzyklen [About]

Wie Sie wissen, kann Swifts Schließung die Instanz erfassen. Dies bedeutet, dass Sie selfin einem Verschluss verwenden können. Insbesondere escaping closure[About] kann ein strong reference cyclewelche erstellen . Übrigens muss man selfdrinnen explizit verwenden escaping closure.

Das schnelle Schließen verfügt über eine Capture ListFunktion, mit der Sie eine solche Situation vermeiden und einen Referenzzyklus unterbrechen können, da Sie keinen starken Bezug zur erfassten Instanz haben. Das Capture List-Element ist ein Paar von weak/ unownedund ein Verweis auf eine Klasse oder Variable.

Beispielsweise

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- bevorzugter, verwenden Sie es, wenn es möglich ist
  • unowned - Verwenden Sie es, wenn Sie sicher sind, dass die Lebensdauer des Instanzeigners länger ist als die Schließung
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.