如何防止调度组不崩溃?

时间:2019-05-26 15:31:43

标签: objective-c macos grand-central-dispatch

我正在使用以下代码来等待异步任务完成。它可以工作几次并崩溃。 updateFromTable总是调用callback(),以便使组呼叫保持平衡,但仍会崩溃。

- (void)updateFromTable:(Table *)table env:(Env *)env callback:(void (^)(void))callback {
    [someasync usingBlock:^{
        callback()
    }];
}

- (NSString * _Nullable)process {
    JSL * __weak weakSelf = self;
    NSString __block *ret = nil;
    dispatch_group_enter(_dispatchGroup);
    dispatch_async(_repQueue, ^{
        JSL *this = weakSelf;
        [this updateFromTable:[this->_env table] env:this->_env callback:^{
            ret = [some op .. ];
            dispatch_group_leave(this->_dispatchGroup);
        }];
    });
    dispatch_group_wait(_dispatchGroup, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC));
    info(@"%@", @"done");
    return ret;
}

有人知道为什么它会随机崩溃吗?如何解决这个问题?基本上,我想要实现的是调用几个异步任务,等待所有异步任务完成,然后再进行其余的工作。


引荐:How to wait past dispatch_async before proceeding?

1 个答案:

答案 0 :(得分:1)

如果->this,则无法使用nil取消引用ivars。因此,典型的解决方案是创建在闭包运行时无法释放的强引用,如果是return,则创建nil

- (NSString * _Nullable)process {
    typeof(self) __weak weakSelf = self;
    [self asynchronousMethodWithCompletion:^{
        typeof(self) strongSelf = weakSelf;
        if (!strongSelf) { return; }

        // can now safely use `strongSelf` here
    });

    ...
}

这是“ weakSelf-strongSelf舞蹈”。您可以在需要确保self不是nil的情况下使用它,例如取消引用ivars(strongSelf->ivar)。

因此:

- (NSString * _Nullable)process {
    typeof(self) __weak weakSelf = self;
    NSString __block *ret = nil;
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(_repQueue, ^{
        typeof(self) strongSelf = weakSelf;
        if (!strongSelf) { return; }

        [strongSelf updateFromTable:[strongSelf->_env table] env:strongSelf->_env callback:^{
            ret = [some op .. ];
            dispatch_group_leave(group);
        }];
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    info(@"%@", @"done");
    return ret;
}

其他一些观察结果:

  • 调度组应该是方法的局部变量,而不是ivar。您的代码中无需引用此group

  • 确保您的dispatch_group_leave调用不超过dispatch_group_enter调用的数量(即,不会多次调用此完成处理程序块)。

    li>
  • 我建议您等待DISPATCH_TIME_FOREVER(假设您希望它真的等待它完成)。

  • 此外,如果这些是属性(我猜它们是在下划线的基础上),那么使用self.env而不是self->_env会更安全,因为它不会如果selfnil会崩溃,而只会返回nil

我必须承认这仍然看起来不正确(例如,如果updateFromTable已经是异步的,为什么要麻烦地将其异步分配给_repQueue;如果它是同步的,那么再次,为什么要异步地分配它只等它)。但是,如果没有看到updateFromTable的实现,就无法进一步发表评论。


或者更好的是,使该方法异步:

- (void)processWithCompletion:(void (^)(NSString *))callback {
    typeof(self) __weak weakSelf = self;
    dispatch_async(_repQueue, ^{
        typeof(self) strongSelf = weakSelf;
        if (!strongSelf) { return; }

        [strongSelf updateFromTable:[strongSelf->_env table] env:strongSelf->_env callback:^{
            NSString *ret = [some op .. ];
            callback(ret);
        }];
    });
}