2012-07-22 17 views
10

Ich verwende eine NSOperationQueue und stehe Schlange NSOperationBlocks. Nun haben Blöcke einen starken Hinweis auf alle Fälle in dem Block, und das anrufende Objekt hat auch einen starken Halt auf dem Block, so hat es geraten worden, so etwas wie die folgenden Funktionen ausführen:Schwache Referenzen innerhalb eines Blocks

__weak Cell *weakSelf = self; 
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ 
     UIImage *image = /* render some image */ 
     /* what if by the time I get here self no longer exists? */ 
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
      [weakSelf setImageViewImage:image]; 
     }]; 
    }]; 
    [self.renderQueue addOperation:op]; 

Also, meine Frage ist, sagen wir mal, dass das Cell Objekt nicht mehr existiert, wenn das Bild fertig ist und die Zeile zurückkommt (es wurde freigegeben, möglicherweise aufgrund der Wiederverwendung von Zellen, was ein wenig schwierig zu formalisieren ist). Wenn ich gehe auf [weakSelf setImageViewImage:] zugreifen, wird das einen EXC_BAD_ACCESS Fehler verursachen?

Derzeit versuche ich zu verfolgen, was die Ursache meines Problems ist, und ich denke, dass es etwas damit zu tun haben könnte.

+0

Hat die Zelle eine starke ref müssen renderQueue? Hat etwas anderes einen starken Bezug zu renderQueue? Wenn Sie die Ursache eines EXC_BAD_ACCESS verfolgen möchten, verwenden Sie einen interaktiven Debugger oder Instrumente. – outis

+0

Die schwache Referenz sollte hier nicht benötigt werden. Unter der Annahme von ARC wird "op" am Ende der Methode, in der sich dieser Code befindet, freigegeben (sie wird auch von der Operationswarteschlange beibehalten/freigegeben, aber das ist im Moment irrelevant). Es würde nur einen Retain-Zyklus geben, wenn Sie sich auf dealloc von 'self' verlassen würden, um den Block freizugeben, d. H. Sie würden ihn in einem Ivar speichern. Das ist hier nicht der Fall, und Sie brauchen sich keine Sorgen zu machen, dass der Block sich selbst zurückhält. –

+0

Wann gibt die Operation Queue den Artikel frei? Nachdem die Ausführung abgeschlossen ist, automatisch? Oder muss ich den Block manuell freigeben? Ich benutze ARC. – Snowman

Antwort

18

Also, __weak ist eine Nullstellung schwache Referenz. Dies bedeutet, dass während Ihrer Operation self tatsächlich freigegeben werden kann, aber alle schwachen Referenzen darauf (nämlich weakSelf) werden auf Null gesetzt. Das bedeutet, dass [weakSelf setImageViewImage:image] gerade eine Nachricht an nil sendet, die sicher ist; oder zumindest sollte es keinen EXC_BAD_ACCESS verursachen. (Übrigens, wenn Sie weakSelf als __unsafe_unretained qualifiziert hatten, könnten Sie Nachrichten an ein befreites Objekt am Ende zu senden.)

Also, ich bezweifle, dass eine Nachricht an eine __weak Referenz sendet einen Absturz verursacht. Wenn Sie sicherstellen möchten, dass self für die Dauer der Operation überlebt, können Sie einen starken Hinweis auf die schwache im Block Umfang erhalten:

__weak Cell *weakSelf = self; 

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ 
    Cell *strongSelf = weakSelf; // object pointers are implicitly __strong 
    // strongSelf will survive the duration of this operation. 
    // carry on. 
}]; 
8

Wenn Sie nicht schwach verwenden, sind Sie ein Erstellen Zyklus beibehalten, aber der Zyklus wird unterbrochen, sobald die Blöcke die Ausführung beendet haben. Ich würde wahrscheinlich nicht schwach hier verwenden.

Wie auch immer, Sie können jede Nachricht an nil senden, und es wird ignoriert. Wenn also die Variable weakSelf auf nil gesetzt wird, weil das Objekt Cell freigegeben wird, wird die Nachricht setImageViewImage: automatisch nichts tun. Es wird nicht abstürzen.

Da Sie die Wiederverwendung von Zellen erwähnen, nehme ich an, dass Ihre Cell eine Unterklasse von UITableViewCell ist. In diesem Fall weist Ihr Beispielcode ein schwerwiegendes Problem auf. UITableViewCell s werden normalerweise nicht freigegeben. Sie werden in die Warteschlange für die Wiederverwendung von Zellen eingefügt. Daher wird Ihre weakSelf-Variable nicht auf Null gesetzt, da eine schwache Referenz nur dann auf Null gesetzt wird, wenn das Objekt tatsächlich freigegeben wird.

Wenn die Zeile [weakSelf setImageViewImage:image] ausgeführt wird, wurde die Zelle möglicherweise erneut verwendet, um eine andere Zeile in der Tabelle darzustellen, und Sie fügen ein falsches Bild in die Zelle ein. Sie sollen Ihren Bild-Rendering-Code aus der Cell Klasse, in der Tabelle nach Ansicht der Datenquellenklasse bewegen:

- (void)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    Cell *cell = // get a cell... 

    [self startLoadingImageForIndexPath:indexPath]; 
    return cell; 
} 

- (void)startLoadingImageForIndexPath:(NSIndexPath *)indexPath { 
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ 
     UIImage *image = [self renderImageForIndexPath:indexPath]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      Cell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; 
      [cell setImageViewImage:image]; 
     }); 
    }]; 
    [self.renderQueue addOperation:op]; 
} 
+0

Warte, was meinst du, Selbst behält die Operationswarteschlange nicht? Es ist eine starke Eigenschaft in meiner Klasse. Oder meintest du etwas anderes? – Snowman

+0

Hoppla. Du hast recht, es behält renderQueue. Aber es ist ein kurzlebiger Zyklus, der automatisch unterbrochen wird, sobald die Blöcke fertig sind. –

+0

Was wäre, wenn ich die Blockoperationen in meinem eigenen Wörterbuch speichern würde, das stark von der Datenquelle gehalten wird, so dass ich die Operationen nach Bedarf abrufen und abbrechen kann. Wie würde das die Dinge verändern? – Snowman

Verwandte Themen