local dispatch_once value unsafe(transient memory)触发警告

时间:2014-09-02 11:41:40

标签: ios clang warnings grand-central-dispatch libdispatch

所以在业余时间我正在处理HTTP请求'引擎'。 我正在尝试构建的是一个“引擎”,它生成对通用对象的请求/解析响应 对于iPhone应用程序。

最重要的是,它应该 _allways _回调用户界面。

无论发生什么(NSURLRequest超时/解析错误/ NSExcept引发) (也许SIG也有一天?)

所以我使用blocks和dispatch_once创建了一些gory。 它有效,但我有这个Clang警告。 我的意思是我的请求有效,如果出现异常,UI显然会被调用一次。

  

调用'dispatch_once'使用局部变量'once'作为   谓词值。使用这种瞬态内存作为谓词是   有潜在危险

这是问题的核心

// invoke a bloc in a proper thread with a proper autorelease / trycatch / logs
static inline void api_dispatch_concurrent(EngineOnceCallback block, SEL caller, EngineCallback callback) {
    dispatch_async(APIengineQueue(), ^{  // serial dispatch_queue
    @autoreleasepool {    

            dispatch_once_t once = 0;

            @try {
                // block may callback using 'once'
                (block) ? block(once) : NULL;
            }

            @catch (NSException *exception) {
            [[ACLogs sharedLogs] logException:exception caller:caller
                                        class:[ACRetroscopeEngine class]];

            APILog(@"NSException invoking API block:%@", exception);
            dispatch_once(&once, ^{
                APILog(@"Perform callback as recover from exception");
                ACResponse *defaultResponse = [ACResponse responseWithException:exception];

                try_catch(block_safe_invoke_main_thread(callback, defaultResponse));
            });
        }
        @finally {

        }
    }
});
}

那里使用了一些宏:

#define block_safe_invoke(block, ...) (block != NULL) ? block(__VA_ARGS__) : NULL;

#define block_safe_invoke_main_thread(block, ...)               \
if ([NSThread isMainThread]) {                                  \
    block_safe_invoke(block, __VA_ARGS__)                       \
} else {                                                        \
    dispatch_sync(dispatch_get_main_queue(), ^{                 \
        block_safe_invoke(block, __VA_ARGS__)                   \
    });                                                         \
}                                                               \

提前致谢:p

1 个答案:

答案 0 :(得分:1)

你曾经'对于dispatch_once块,变量应该是静态的 - 即

  static dispatch_once_t once = 0;

这将确保您的代码只执行一次恢复回调。实际上,你可以只使用一个静态布尔值 - 你实际上并不需要一个dispatch_once块,因为你要调度到一个串行队列(根据你的评论)。但是,这是一种很好的做法。

正如您的代码当前所示,每个异常都会执行一次恢复回调,这意味着如果您向APIEngineQueue分配两个块,理论上两个块都可以执行回调 - 因为每个块都有自己的本地'一次& #39;变量。换句话说,您的dispatch_once根本没有任何效果,它与写作相同:

    @catch (NSException *exception) {
        [[ACLogs sharedLogs] logException:exception caller:caller
                                    class:[ACRetroscopeEngine class]];

        APILog(@"NSException invoking API block:%@", exception);
        APILog(@"Perform callback as recover from exception");
        ACResponse *defaultResponse = [ACResponse responseWithException:exception];
        try_catch(block_safe_invoke_main_thread(callback, defaultResponse));

block_safe_invoke_main_thread也有点复杂,因为你知道你不在主线程上,所以为什么要测试呢?