it-swarm.com.de

Immer eine schwache Selbstreferenz in den Block in ARC übergeben?

Ich bin etwas verwirrt über die Blockverwendung in Objective-C. Ich verwende derzeit ARC und habe eine ganze Reihe von Blöcken in meiner App, die sich derzeit immer auf self anstatt auf dessen schwachen Verweis beziehen. Kann das die Ursache dafür sein, dass diese Blöcke self behalten und die Zuordnung aufheben? Die Frage ist, sollte ich immer eine weak Referenz von self in einem Block verwenden?

-(void)handleNewerData:(NSArray *)arr
{
    ProcessOperation *operation =
    [[ProcessOperation alloc] initWithDataToProcess:arr
                                         completion:^(NSMutableArray *rows) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateFeed:arr rows:rows];
        });
    }];
    [dataProcessQueue addOperation:operation];
}

ProcessOperation.h

@interface ProcessOperation : NSOperation
{
    NSMutableArray *dataArr;
    NSMutableArray *rowHeightsArr;
    void (^callback)(NSMutableArray *rows);
}

ProcessOperation.m

-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{

    if(self =[super init]){
        dataArr = [NSMutableArray arrayWithArray:data];
        rowHeightsArr = [NSMutableArray new];
        callback = cb;
    }
    return self;
}

- (void)main {
    @autoreleasepool {
        ...
        callback(rowHeightsArr);
    }
}
246
the_critic

Es ist hilfreich, sich nicht auf den Teil strong oder weak der Diskussion zu konzentrieren. Konzentrieren Sie sich stattdessen auf den Teil des Zyklus .

Ein Beibehaltungszyklus ist eine Schleife, die auftritt, wenn Objekt A Objekt B, und Objekt B behält Objekt A bei. Wenn in dieser Situation eines der Objekte freigegeben wird:

  • Objekt A wird nicht freigegeben, da Objekt B einen Verweis darauf enthält.
  • Objekt B wird jedoch niemals freigegeben, solange Objekt A einen Verweis darauf hat.
  • Objekt A wird jedoch niemals freigegeben, da Objekt B einen Verweis darauf enthält.
  • ad infinitum

Auf diese Weise bleiben diese beiden Objekte für die gesamte Lebensdauer des Programms im Speicher, obwohl sie freigegeben werden sollten, wenn alles ordnungsgemäß funktioniert.

Wir machen uns also Sorgen über die Beibehaltung von Zyklen , und es gibt nichts über Blöcke an und für sich, die diese Zyklen erzeugen. Dies ist kein Problem, zum Beispiel:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

Der Block behält self bei, aber self behält den Block nicht bei. Wenn der eine oder andere freigegeben wird, wird kein Zyklus erstellt und alles wird wie gewünscht freigegeben.

Wo Sie in Schwierigkeiten geraten, ist etwas wie:

//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);

//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [self doSomethingWithObj:obj];     
}];

Jetzt hat Ihr Objekt (self) einen expliziten strong Verweis auf den Block. Und der Block hat einen impliziten starken Verweis auf self. Das ist ein Zyklus, und jetzt wird keines der Objekte ordnungsgemäß freigegeben.

Da in einer solchen Situation self per Definition bereits einen strong-Verweis auf den Block hat, ist dies normalerweise der Fall Dies lässt sich am einfachsten beheben, indem der zu verwendende Block explizit schwach auf self verweist:

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];     
}];

Dies sollte jedoch nicht das Standardmuster sein, dem Sie folgen wenn Sie mit Blöcken arbeiten, die self aufrufen! Dies sollte nur verwendet werden, um einen anderen Haltezyklus zwischen sich selbst und dem Block zu unterbrechen. Wenn Sie dieses Muster überall übernehmen, laufen Sie Gefahr, einen Block an etwas zu übergeben, das ausgeführt wurde, nachdem die Zuordnung von self aufgehoben wurde.

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not retained!
  [weakSelf doSomething];
}];
700
jemmons

Sie müssen nicht immer eine schwache Referenz verwenden. Wenn Ihr Block nicht beibehalten, sondern ausgeführt und dann verworfen wird, können Sie sich selbst stark erfassen, da dadurch kein Aufbewahrungszyklus erstellt wird. In einigen Fällen möchten Sie sogar, dass der Block das Selbst bis zur Fertigstellung des Blocks hält, damit die Zuordnung nicht vorzeitig aufgehoben wird. Wenn Sie den Block jedoch stark erfassen und sich selbst erfassen, wird ein Beibehaltungszyklus erstellt.

25
Leo Natan

Ich stimme voll und ganz mit @jemmons überein.

"Dies sollte jedoch nicht das Standardmuster sein, dem Sie bei der Behandlung von Blöcken folgen, die self aufrufen. Dies sollte nur verwendet werden, um den sonst üblichen Beibehaltungszyklus zwischen self und dem Block zu unterbrechen. Wenn Sie dieses Muster überall übernehmen würden, würden Sie Es besteht die Gefahr, dass ein Block an etwas übergeben wird, das ausgeführt wurde, nachdem die Zuordnung zu "self" aufgehoben wurde. "

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not  retained!
  [weakSelf doSomething];
}];

Um dieses Problem zu lösen, kann innerhalb des Blocks eine starke Referenz über das schwache Selbst definiert werden.

__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
   MyObject *strongSelf = weakSelf;
  [strongSelf doSomething];
}];
23
Ilker Baltaci

Wie Leo betont, würde der Code, den Sie Ihrer Frage hinzugefügt haben, keinen starken Referenzzyklus vorschlagen (a.k.a., Beibehaltungszyklus). Ein betriebsbezogenes Problem, das einen starken Referenzzyklus verursachen könnte, ist, wenn der Betrieb nicht freigegeben wird. Ihr Code-Snippet weist zwar darauf hin, dass Sie Ihre Operation nicht als gleichzeitig definiert haben. Wenn dies jedoch der Fall ist, wird sie nicht freigegeben, wenn Sie nie isFinished gepostet haben oder wenn Sie zirkuläre Abhängigkeiten hatten oder ähnliches. Und wenn die Operation nicht freigegeben wird, wird auch der Ansichtscontroller nicht freigegeben. Ich würde vorschlagen, einen Haltepunkt oder NSLog in die dealloc -Methode Ihrer Operation einzufügen und zu bestätigen, dass diese aufgerufen wird.

Du sagtest:

Ich verstehe den Begriff der Retain-Zyklen, bin mir aber nicht ganz sicher, was in Blöcken passiert, was mich ein wenig verwirrt

Die Probleme mit dem Aufbewahrungszyklus (starker Referenzzyklus), die bei Blöcken auftreten, sind genau wie die Probleme mit dem Aufbewahrungszyklus, die Sie kennen. Ein Block behält starke Verweise auf alle Objekte bei, die im Block erscheinen, und gibt diese starken Verweise erst frei, wenn der Block selbst freigegeben wird. Wenn also ein Block auf self verweist oder nur auf eine Instanzvariable von self verweist, die einen starken Bezug zu self beibehält, wird dieser erst aufgelöst, wenn der Block freigegeben wird (oder in diesem Fall bis die Unterklasse NSOperation freigegeben wird.

Weitere Informationen finden Sie im Abschnitt Vermeiden starker Referenzzyklen beim Erfassen von Selbst des Abschnitts Programmieren mit Objective-C: Arbeiten mit Blöcken . dokumentieren.

Wenn Ihr View Controller immer noch nicht freigegeben wird, müssen Sie lediglich identifizieren, wo sich die nicht aufgelöste starke Referenz befindet (vorausgesetzt, Sie haben bestätigt, dass die Zuordnung von NSOperation aufgehoben wird). Ein häufiges Beispiel ist die Verwendung eines sich wiederholenden NSTimer. Oder ein benutzerdefiniertes delegate oder ein anderes Objekt, das fälschlicherweise eine strong Referenz verwaltet. Sie können häufig Instrumente verwenden, um herauszufinden, wo Objekte ihre starken Referenzen erhalten, z.

record reference counts in Xcode 6

Oder in Xcode 5:

record reference counts in Xcode 5

19
Rob

Einige Erklärungen ignorieren eine Bedingung bezüglich des Aufbewahrungszyklus. [Wenn eine Gruppe von Objekten durch einen Kreis starker Beziehungen verbunden ist, halten sie sich gegenseitig am Leben, auch wenn es keine starken Referenzen von außerhalb der Gruppe gibt.] Weitere Informationen finden Sie unter - Dokument

0
Danyun Liu