5

Mam niektóre rekursywny kod blokowy w celu-c, który powoduje błąd EXC_BAD_ACCESS.Objective-c rekursywne bloki z wątkami 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); 
} 

Błąd występuje na słabym bloku (str), który zakładam, ponieważ został zwolniony po wywołaniu funkcji dispatch_async. Wywołanie strong_block (STR) w to miejsce, gdy jest zadeklarowana z __block tak:

__block void(^strong_block)(NSString *); 

Powoduje ostrzeżenie strong_block „przechwytywanie«»zdecydowanie w tym bloku jest prawdopodobne, aby doprowadzić do zatrzymania cyklu”.

Więc zmieniłem metodę testBlock nie używać słabe odniesienie tak:

- (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); 
} 

Ale jestem pewien, czy to powoduje, że zachowują cykl czy dodanie

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

wewnątrz bloku zamiast spowoduje również cykl zatrzymania. Jaki jest właściwy sposób radzenia sobie z tą sytuacją?

+1

Mój mózg boli od patrzenia na to .. –

Odpowiedz

1

To awarii, ponieważ blok (wskazywanego przez weak_block, strong_block) został już zwalniane przez czas „zakończenie” działa bloku i wywołanie bloku o nil awarii bloku wskaźnika.

Blok został zwolniony, ponieważ po otrzymaniu testBlocks nie ma silnych odniesień.

Drugi będzie miał cykl zatrzymania, ponieważ blok przechwytuje inner_block, który ma silne odniesienie do samego siebie.

Właściwym sposobem jest, aby silne odniesienia z przechwyconego słabego odniesienia wewnątrz bloku, i niech przechwytywanie bloków zakończenie, że:

- (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

To działało dla mnie, mój błąd był używany: typeof (weak_block) inner_block = weak_block; zamiast void (^ inner_block) (NSString *) = weak_block; co sprawiło, że było słabe zamiast silnego. – malhal

0

Nie wiem, to jest dowodem zarówno możliwości, ale jeśli dodać weak właściwość bloku i sprawdzić, że jeden blok rzeczy mimo wszystko prowadzi ...

... 
@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 
    }); 
} 

W moim teście blok nie jest zwalniane, a także adres pamięci pozostaje taki sam w czasie, nawet jeśli zmienisz regularne przypisywanie dwóch silnych bloków, aby użyć kopii zamiast niejawnie zachowanego przypisania.

+0

Hej Myślę, że bycie właściwość klasy true_weak_block nie zostanie wydana, dopóki ta klasa nie zostanie wydana poprawnie? czy po prostu myliłem się tutaj? – richy

+0

Jeśli działa jak zwykły obiekt, to działałby zgodnie z oczekiwaniami, ponieważ przypisanie do słabego nie wywoła refkacji, więc gdy pierwotne odniesienie wykracza poza zakres, zostanie zwolniony. –

+0

Zaktualizowany testem s przy użyciu zwykłego obiektu. –