5

Ich habe einige rekursive Blockcode in Ziel-c, die einen Fehler EXC_BAD_ACCESS verursacht.Objective-c rekursive Blöcke mit Threads EXC_BAD_ACCESS

- (void) doSomethingWithCompletion:(void (^)())completion { 
    if (completion) { 
     dispatch_async(dispatch_get_main_queue(), completion); 
    } 
} 

- (void) testBlocks { 

    NSString *testString = @"hello"; 

    __block NSInteger count = 0; 

    __block __weak void (^weak_block)(NSString *); 
    void(^strong_block)(NSString *); 
    weak_block = strong_block = ^(NSString *str) { 

     [self doSomethingWithCompletion:^{ 
      NSLog(@"number: %zd", count); 
      if (++count < 10) { 
       weak_block(str); 
      } 
     }]; 


    }; 
    strong_block(testString); 
} 

Der Fehler tritt auf weak_block (str), die ich nehme an, weil es freigegeben wird, wenn dispatch_async genannt wird. Der Aufruf strong_block (str) in seinem Platz, wenn es mit __block wie so erklärt hat:

__block void(^strong_block)(NSString *); 

Verursacht eine Warnung ‚Capturing‚strong_block‘in diesem Block stark wird wahrscheinlich zu einem behalten Zyklus führen‘.

So änderte ich den Testblock Methode nicht eine schwache Referenz wie so verwenden:

- (void) testBlocks { 

    NSString *testString = @"hello"; 

    __block NSInteger count = 0; 

    __block void (^inner_block)(NSString *); 
    void(^strong_block)(NSString *); 
    inner_block = strong_block = ^(NSString *str) { 

     [self doSomethingWithCompletion:^{ 
      NSLog(@"number: %zd", count); 
      if (++count < 10) { 
       inner_block(str); 
      } 
     }]; 


    }; 
    strong_block(testString); 
} 

Aber ich bin nicht sicher, ob dies ein Zyklus beibehalten verursacht oder wenn das Hinzufügen

__block void (^inner_block)(NSString *) = weak_block; 

innerhalb des Blocks statt würde auch einen Rückhaltezyklus verursachen. Wie ist der richtige Umgang mit dieser Situation?

+1

Mein Gehirn bei dieser von der Suche verletzt .. –

Antwort

1

Es stürzt ab, weil der Block (auf dem durch weak_block, strong_block) wird bereits von der Zeit, die „Vollendung“ Block läuft, und rief einen Block mit einem nil Blockzeiger Abstürzen freigegeben worden.

Der Block wird freigegeben, da keine starken Referenzen darauf vorhanden sind, nachdem testBlocks zurückgegeben wurde.

Der zweite wird einen Retain-Zyklus haben, da der Block inner_block erfasst, was einen starken Bezug zu sich selbst darstellt.

Der richtige Weg ist innerhalb des Blocks eine starke Referenz aus dem erfassten schwachen Verweis zu machen, und lassen Sie den Abschluss-Block-Capture, die:

- (void) testBlocks { 

    NSString *testString = @"hello"; 

    __block NSInteger count = 0; 

    __block __weak void (^weak_block)(NSString *); 
    void(^strong_block)(NSString *); 
    weak_block = strong_block = ^(NSString *str) { 

     void(^inner_block)(NSString *) = weak_block; 
     [self doSomethingWithCompletion:^{ 
      NSLog(@"number: %zd", count); 
      if (++count < 10) { 
       inner_block(str); 
      } 
     }]; 


    }; 
    strong_block(testString); 
} 
+0

Das funktionierte für mich, mein Fehler war ich: typeof (schwach_block) inner_block = schwach_block; statt void (^ inner_block) (NSString *) = weak_block; das machte es schwach statt stark. – malhal

0

nicht sicher, dies ist ein Beweis für beiden Möglichkeiten, aber wenn Sie eine weak Block Eigenschaft hinzufügen und dass man untersuchen, nachdem alle Block Sachen laufen haben ...

... 
@property (weak) void (^true_weak_block)(NSString *); 
@property (weak) NSString *weak_string; 
... 


- (void) testBlocks { 

NSString *strong_string = [NSString stringWithFormat:@"%@", @"some string"]; // note that you can not use a string literal in this example.. 
self.weak_string = strong_string;  

NSString *testString = @"hello"; 

__block NSInteger count = 0; 

__block void (^inner_block)(NSString *); 
void(^strong_block)(NSString *); 
inner_block = strong_block = ^(NSString *str) { 

    [self doSomethingWithCompletion:^{ 
     NSLog(@"number: %zd", count); 
     if (++count < 10) { 
      inner_block(str); 
     } 
    }]; 


}; 
self.true_week_block = strong_block; 
[self test]; 
strong_block(testString); 
} 

- (void)test { 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     NSLog(@"%@", self.true_week_block); // not deallocated 
     NSLog(@"%@", self.weak_string); // deallocated 
    }); 
} 

In meinem Test der Block nie freigegeben wird, und auch Die Speicheradresse bleibt im Laufe der Zeit gleich, auch wenn Sie die reguläre Zuweisung der beiden starken Blöcke ändern, um Kopie anstelle der implizit beibehaltenen Zuweisung zu verwenden.

+0

Hey Ich denke, dass eine Eigenschaft der Klasse der True_weak_block würde nicht freigegeben werden, bis diese Klasse freigegeben wurde richtig? oder verwirre ich mich hier nur? – richy

+0

Wenn es wie ein normales Objekt funktioniert, würde es hier wie erwartet funktionieren, da die Zuweisung zu schwach den Refcount nicht verpuffen würde. Wenn die ursprüngliche Referenz den Gültigkeitsbereich verlässt, wird sie aufgehoben. –

+0

Aktualisiert mit s test mit einem regulären Objekt. –