Objective-c 带线程的递归块 EXC_BAD_ACCESS

Objective-c recursive blocks with threads EXC_BAD_ACCESS

我在 objective-c 中有一些递归块代码导致 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);
}

错误发生在 weak_block(str) 上,我认为这是因为它在调用 dispatch_async 时被释放。在用 __block 声明时调用 strong_block(str) 就像这样:

__block void(^strong_block)(NSString *);

导致警告“在此块中强烈捕获 'strong_block' 可能会导致保留周期”。

所以我更改了 testBlock 方法,使其不使用像这样的弱引用:

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

但我不确定这是否会导致保留周期或添加

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

在块内部也会导致保留循环。处理这种情况的正确方法是什么?

不确定这是否是任何一种可能性的证据,但如果您添加一个 weak 块 属性 并在所有块内容具有 运行...

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

在我的测试中,该块永远不会被释放,内存地址也会随着时间的推移保持不变,即使您将两个强块的常规分配更改为使用复制而不是隐式保留分配也是如此。

它崩溃是因为块(由 weak_blockstrong_block 指向)在 "completion" 块运行时已经被释放,并且使用 nil 块指针崩溃。

块被释放,因为在 testBlocks returns.

之后没有对它的强引用

第二个将有一个保留周期,因为该块捕获了 inner_block,它持有对自身的强引用。

正确的方法是从块内捕获的弱引用做一个强引用,让完成块捕获:

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