4

このクラッシュはかなりまれですが、私が間違っていると確信するのに十分な頻度で発生しています。

これは、メイン スレッドの非同期ディスパッチとバリア ディスパッチの両方をカスタム同時キューで使用して実行される API 呼び出しです (同じキューへの非バリア呼び出しを介して、他の場所で読み取られるデータを変更するため、バリアが使用されます)。

目的は、HTTP リクエストを非同期で行い、dispatch_barrier_async を起動して結果のデータを処理することです。

クラッシュは、dispatch_barrier_async 呼び出しが、渡されたブロックで使用される変数をコピーしているときに発生しています。変数はブロックで使用される前に割り当てが解除されていると思いますが、それを (__block 変数として) 宣言する方法を考えると、それがどのように発生するかわかりません (問題がブロックの反対側にある場合を除きます)。代入演算子...)。

スタック トレースは次のとおりです。


#0  0x00004f44 in __Block_byref_object_copy_ at /blah/ABEvent.m:156
#1  0x0000582d in __copy_helper_block_ at /blah/ABEvent.m:191
#2  0x02cf3be2 in _Block_call_copy_helper ()
#3  0x02cf3681 in _Block_copy_internal ()
#4  0x02c25526 in _dispatch_Block_copy ()
#5  0x02c26802 in dispatch_barrier_async ()
#6  0x00004e71 in __18+[ABEvent fetch]_block_invoke at /blah/ABEvent.m:159
#7  0x00056e77 in __88-[ABClient get:parameters:success:failure:]_block_invoke_2 at /blah/ABClient.m:375
#8  0x02c2553f in _dispatch_call_block_and_release ()
#9  0x02c37014 in _dispatch_client_callout ()
#10 0x02c277d5 in _dispatch_main_queue_callback_4CF ()
#11 0x02facaf5 in __CFRunLoopRun ()
#12 0x02fabf44 in CFRunLoopRunSpecific ()
#13 0x02fabe1b in CFRunLoopRunInMode ()
#14 0x02f517e3 in GSEventRunModal ()
#15 0x02f51668 in GSEventRun ()
#16 0x01ef1ffc in UIApplicationMain ()
#17 0x0005164d in main at /blah/main.m:3
#18 0x00002db5 in start ()

そしてコード:


+ (void)fetch {
    ABBlock _success = ^(ABMessage *m) {

        __block NSMutableArray *fetched = [NSMutableArray arrayWithArray:m.params[@"live"]];
        [fetched addObjectsFromArray:m.params[@"soon"]]; // EXC_BAD_ACCESS (top of stack)

        dispatch_barrier_async([ABEvent eventQueue], ^{  // CRASHED IN BLOCK INVOKE (stack line 6)
            NSMutableArray *events = [NSMutableArray array];

            for (NSDictionary *d in fetched) {
                [events addObject:[ABEvent eventWithDictionary:d]];
            }

            AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
            appDelegate.events = events;

            events = nil;
        });
    };

    [[ABClient sharedInstance] events:_success failure:nil];
}

上流の:



-(void)events:(ABBlock)success failure:(ABBlock)failure {
    NSString *params = [NSString stringWithFormat:@"%@",[ABUser loggedInUser]? [ABUser loggedInUser].name : @"no"];
    NSDictionary *dict = @{@"loggedIn": params};

    [self get:@"events/live.json" parameters:dict success:success failure:failure];
}

- (void)get:(NSString *)path
 parameters:(NSDictionary *)parameters
    success:(ABBlock)success
    failure:(ABBlock)failure

    __block ABBlock blockSuccess = success;
    __block ABBlock blockFailure = failure;
    NSString *blockPath = path;
    NSDictionary *blockParameters = parameters;

    AFHTTPSuccessBlock _success = ^(AFHTTPRequestOperation *request, id response) {
        if (blockSuccess) {
            ABMessage *msg = [ABMessage messageWithObject:response];
            dispatch_async(dispatch_get_main_queue(), ^(void) {
                blockSuccess(msg);
            });
        }

    };
}

Elsewhere:

typedef void (^ABBlock) (ABMessage *);

変数が barrier_async ブロックによってどのように使用されているかについて明らかに間違っている点はありますか? 引数として渡されるメッセージ (*m) をコピーする必要があるかどうか疑問に思っています。

4

3 に答える 3

1

それへの強力な参照を作成してみてください。

        ABMessage *msg = [ABMessage messageWithObject:response];
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            ABMessage *m = msg;
            blockSuccess(m);
        });
于 2013-04-30T00:24:56.687 に答える
0

手動で次のようにして、このブロックをヒープにコピーしてみてください。

[[ABClient sharedInstance] events:[_success copy] failure:nil];

success別のブロックを呼び出しているためdispatch_async、宣言したブロックを+fetchディスパッチする前にヒープにコピーする必要があります。

于 2013-05-02T11:35:38.937 に答える