如何防止调度组不崩溃?

How to prevent dispatch group to not crash?

我正在使用以下代码等待异步任务完成。它工作了几次并崩溃了。 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?

如果 thisnil,则不能使用 -> 取消引用 ivar。因此,典型的解决方案是创建在闭包运行时无法释放的强引用,并且 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 调用的次数(即此完成处理程序块未被多次调用)。

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

  • 此外,如果这些是属性(我猜它们是基于下划线),那么使用 self.env 而不是 self->_env 更安全,因为如果 selfnil,它不会崩溃,而只会 return 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);
        }];
    });
}