I have some recursive block code in objective-c that is causing a EXC_BAD_ACCESS error.
- (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);
}
The error happens on weak_block(str) which i assume is because it is released when dispatch_async is called. Calling strong_block(str) in it's place when it's declared with __block like so:
__block void(^strong_block)(NSString *);
Causes a warning 'Capturing 'strong_block' strongly in this block is likely to lead to a retain cycle'.
So I changed the testBlock method to not use a weak reference like so:
- (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);
}
But I am unsure if this causes a retain cycle or if adding
__block void (^inner_block)(NSString *) = weak_block;
inside the block instead would cause a retain cycle as well. What is the correct way to handle this situation?
It crashes because the block (pointed to by weak_block
, strong_block
) has already been deallocated by the time the "completion" block runs, and calling a block with a nil
block pointer crashes.
The block is deallocated because there are no strong references to it after testBlocks
returns.
The second one will have a retain cycle because the block captures inner_block
, which holds a strong reference to itself.
The proper way is to make a strong reference from the captured weak reference inside the block, and let the completion block capture that:
- (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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With