具有线程EXC_BAD_ACCESS的Objective-c递归块

时间:2015-07-23 12:25:54

标签: objective-c recursion objective-c-blocks grand-central-dispatch

我在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;
在块内部

也会导致保留周期。处理这种情况的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

崩溃是因为块(由weak_blockstrong_block指向)已经在&#34;完成&#34;之前被释放了。块运行,并调用带有nil块指针的块崩溃。

该块已取消分配,因为testBlocks返回后没有强引用。

第二个将有一个保留周期,因为该块捕获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);
}

答案 1 :(得分:0)

不确定这是否存在任何可能性的证明,但如果您添加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
    });
}

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