GCD异步块& EXC_BAD_ACCESS

时间:2013-04-29 19:02:56

标签: ios asynchronous grand-central-dispatch

这次崩溃是相当罕见的,但它经常发生,足以说服我,我做错了。

这是在自定义并发队列上使用主线程异步调度和屏障调度执行的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)。

3 个答案:

答案 0 :(得分:1)

尝试创建一个强引用,例如:

        ABMessage *msg = [ABMessage messageWithObject:response];
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            ABMessage *m = msg;
            blockSuccess(m);
        });

答案 1 :(得分:0)

  • 您不需要此行上的__block说明符:

    __block NSMutableArray *fetched = [NSMutableArray arrayWithArray:m.params[@"live"]];
    

为Obj-C对象指定__block说明符时,不会自动保留它们。由于您不需要fetched在块内变异,因此您无需指定__block。 (额外的问题:如果你想覆盖fetched数组的值,你会怎么做?)

  • 您不需要在successfailure块上指定__block:

    ABBlock blockSuccess = success;
    ABBlock blockFailure = failure;
    

应该足够了。复制_success块后,它将依次复制成功和失败块。我甚至不确定如果你在一个区块上指定__block说明符会发生什么(也许它是一个很好的练习 - 这是一个很好的练习),但是没有需要它。

附注:无需复制ABMessage *msg - 块会添加强引用。

答案 2 :(得分:0)

尝试通过手动执行以下操作将此块复制到堆中:

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

由于您在另一个success中调用dispatch_async,因此必须在发送之前将您在+fetch中声明的块复制到堆中。