__block 变量赋值是线程安全的,可以在块后立即读取吗?

Is a __block variable assignment thread-safe to read immediately after the block?

__block NSHTTPURLResponse *httpResponse;

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (!error) 
        httpResponse = (NSHTTPURLResponse *)response;
    }

    dispatch_semaphore_signal(semaphore);
}];
[task resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

之后阅读 httpResponse 安全吗?信号量等待块竞争执行。如果没有错误,是否会立即看到赋值,还是我必须在块外同步或创建内存屏障?

是否等待信号量隐式执行一些同步,这使得 __block 变量可以安全地立即读取。如果这是用 Java 中的 Thread.join() 而不是信号量完成的,那将是安全的,因为它保证了与 "block".

中赋值的先行关系

简短的回答是肯定的。

信号量锁本质上是强制当前正在运行的线程停止执行,直到它收到足够的解锁信号才能继续。

在允许信号量继续执行之前,您定义的变量在其他线程上被修改,因此您的赋值应该是安全发生的。

看来dispatch_semaphore_wait也是一个内存屏障,所以可以安全读取值。

严格来说,此代码将阻塞在执行线程(可能是主线程)上,直到发出信号量锁信号为止。所以 - 简短的回答,是的,它应该可以工作,但这不是最佳实践,因为它会阻塞主线程。

更长的答案:

是的,信号量将确保 __block 捕获的存储在被填充之前不会被访问。但是,调用线程将被等待直到块完成。这并不理想 - 确保 Activity 指标旋转等正常 UI 任务不会发生。

最佳做法是在主对象完成后让块向主对象发出信号(可能使用 dispatch_async 调用主队列),并且仅在此之后访问它。考虑到如果您的会话任务失败(例如,由于网络连接失败),调用线程可能会阻塞,直到调用完成处理程序并出现超时错误,这一点尤其正确。这对用户来说就像应用程序已冻结一样,他们对此无能为力,只能终止应用程序。

有关使用块的更多信息,请参阅:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html

特别是数据会话任务的最佳实践:

http://www.raywenderlich.com/51127/nsurlsession-tutorial