0

そのため、暇なときに HTTP リクエストの「エンジン」に取り組んでいます。私が構築しようとしているのは、iPhone アプリの汎用オブジェクトに対する要求/解析応答を生成する「エンジン」です。

そして何よりも、_常に_ UI をコールバックする必要があります

何が起こったとしても (NSURLRequest のタイムアウト / 解析エラー / NSExcept の発生) (おそらく SIG もいつか ?)

だから私はブロックとdispatch_onceを使って恐ろしいものを作りました。動作しますが、この Clang 警告が表示されます。つまり、リクエストが機能し、例外が発生した場合、UI は明らかに 1 回呼び出されます。

「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

4

1 に答える 1

1

「once」変数は、dispatch_once ブロックに対して静的である必要があります-つまり

  static dispatch_once_t once = 0;

これにより、コードがリカバリ コールバックを 1 回だけ実行するようになります。実際には、静的ブール値だけで逃げることができます.シリアルキューにディスパッチしているため、実際にはdispatch_onceブロックは必要ありません(コメントによると)。しかし、それは良い習慣です。

コードの現状では、リカバリ コールバックは例外ごとに 1 回実行されます。つまり、APIEngineQueue に 2 つのブロックをディスパッチすると、理論上は両方のブロックがコールバックを実行できます。各ブロックには独自のローカル 'once' 変数があるためです。言い換えれば、あなたの 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 も少し複雑です。メイン スレッドを使用していないことがわかっているため、テストする必要はありません。

于 2014-09-02T20:15:16.497 に答える