локальное значение dispatch_once небезопасно (временная память) вызывает предупреждение

Поэтому в свободное время я работаю над «движком» HTTP-запросов. То, что я пытаюсь создать, - это «движок», который генерирует ответ на запрос/анализ общего объекта для приложения iphone.

И самое главное, он должен _всегда_ вызывать пользовательский интерфейс.

Что бы ни случилось (тайм-аут NSURLRequest/ошибка синтаксического анализа/возбуждение NSExcept) (может быть, когда-нибудь и SIG?)

Поэтому я создал что-то кровавое, используя блоки и dispatch_once. Это работает, но у меня есть это предупреждение Clang. Я имею в виду, что мои запросы работают, и в случае исключения пользовательский интерфейс вызывается один раз, по-видимому.

Вызов «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__)                   \
    });                                                         \
}                                                               \

Заранее спасибо :р


person adrian Coye    schedule 02.09.2014    source источник


Ответы (1)


Ваша переменная 'once' должна быть статической для блока dispatch_once, т.е.

  static dispatch_once_t once = 0;

Это гарантирует, что ваш код выполнит обратный вызов восстановления только один раз. На практике вы можете обойтись только статическим логическим значением - вам на самом деле не нужен блок dispatch_once, потому что вы отправляете в последовательную очередь (согласно вашему комментарию). Тем не менее, это хорошая практика.

В текущем состоянии вашего кода ваш обратный вызов восстановления будет выполняться один раз для каждого исключения, что означает, что если вы отправляете два блока в APIEngineQueue, оба блока теоретически могут выполнять обратный вызов, поскольку каждый блок имеет свою собственную локальную переменную «один раз». Другими словами, на данный момент ваш 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 тоже немного запутан, так как вы знаете, что находитесь не в основном потоке, так зачем тестировать его?

person Airsource Ltd    schedule 02.09.2014