ARC: Wenn nil an ein Objekt gesendet wird, wird dessen Dealloc nicht sofort aufgerufen

8

Ich gehe von manueller Speicherverwaltung zu ARC über und habe ein Problem. Meistens führe ich das Laden von Daten asynchron aus, indem ich performSelectorInBackground in meinen Modellklassen aufruft. Die Sache ist, dass ich jede Modellcodeausführung stoppen muss, wenn das Modell null empfängt (Freigabe). In Nicht-Arc war alles einfach - sobald ein Benutzer das Fenster schließt, beginnt sein Controller, sich selbst zu entziehen und sein Modell freizugeben [_myModel release], und so beendet das Modell seine Code-Ausführung (Laden von Daten) und ruft seine Dealloc-Methode auf .

Dies scheint bei ARC anders zu sein. Das Modell führt den Code auch nach dem Empfang der NULL-Nachricht vom Controller aus. Ihre dealloc-Methode wird nur nach ihrer Code-Ausführung (data load) aufgerufen. Dies ist ein Problem, da die Codeausführung so schnell wie möglich beendet werden sollte, wenn ein Benutzer das Fenster (Controller) schließt. Es ist eine Art von mangelnder Kontrolle über den Code - Controller sagt zu modellieren - "Geh weg, ich brauche deine Arbeit nicht mehr", aber das Modell arbeitet immer noch "um seinen Job zu erledigen" :).

Stellen Sie sich vor, ein Modell führt eine sehr schwere Datenverarbeitung mit einer Dauer von 10 Sekunden durch. Ein Modell beginnt mit der Verarbeitung, wenn ein Benutzer das Fenster (Controller) öffnet. Aber ein Benutzer ändert seine Meinung und schließt das Fenster, gleich nach dem Öffnen. Das Modell führt immer noch eine verschwenderische Verarbeitung durch. Irgendwelche Ideen, wie man das löst oder umgeht? Ich mag keine Idee, eine spezielle BOOL "shouldDealloc" -Eigenschaft in meinem Modell zu haben und in der Controller-Dealloc-Methode auf YES zu setzen und in meinen Modellklassenbedingungen zu verwenden. Gibt es eine elegantere Lösung?

Ich habe ein Demo-Projekt gemacht, um das Problem zu zeigen. Zum Testen erstellen Sie einfach eine einzelne Ansicht und fügen Sie den Code ein. Erstellen Sie Schaltflächen - "Berechnen starten" und "Berechnung beenden" in der ViewController.xib-Datei und verbinden Sie ihre IBActions mit startCalculationPressed und stopCalculationPressed :

ViewController.h

%Vor%

ViewController.m

%Vor%

Fügen Sie dem Projekt eine neue MyModel-Klasse hinzu:

MyModel.h

%Vor%

MyModel.m

%Vor%

UPDATE Martin hat Recht, performSelectorOnMainThread behält immer selbst, es gibt also keine Möglichkeit, die Ausführung von Code in anderen Threads (sowohl in ARC als auch in ARC) zu stoppen. Daher wird dealloc nicht sofort beim Freigeben des Modells aufgerufen . Dies sollte also explizit mit der entsprechenden Eigenschaft (zum Beispiel Delegate) mit konditionaler Überprüfung geschehen.

    
Centurion 13.04.2013, 07:59
quelle

1 Antwort

6

Ein Objekt wird freigegeben, wenn die Anzahl seiner Releases auf Null oder in der ARC-Sprache auf 0 sinkt Die letzte starke Referenz auf dieses Objekt ist weg.

%Vor%

fügt einen starken Verweis auf self hinzu, was erklärt, warum das Objekt nicht freigegeben wird bevor der Hintergrund-Thread beendet ist.

Es gibt keinen Weg (den ich kenne), einen Hintergrund-Thread "automatisch" anzuhalten. Gleiches gilt für Bausteine, die mit dispatch_async() oder für NSOperation gestartet wurden. Nach dem Start muss der Thread / Block / Vorgang eine Eigenschaft an Punkten überwachen wo es sicher ist zu stoppen.

In Ihrem Beispiel könnten Sie self.delegate überwachen. Wenn das nil wird, ist niemand Interesse an dem Ergebnis mehr, so kann der Hintergrund Thread zurückkehren. In diesem Fall, Es wäre sinnvoll, die Eigenschaft delegate als atomic zu deklarieren.

Beachten Sie, dass self.delegate auch automatisch auf nil gesetzt wird, wenn der View-Controller wird freigegeben (weil es eine schwache Eigenschaft ist), selbst wenn stopCalculationPressed nicht vorhanden ist wurde angerufen.

    
Martin R 13.04.2013, 09:22
quelle