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
  • 您不需要__block此行的说明符:

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

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

  • 您不需要指定 __block onsuccessfailureblocks:

    ABBlock blockSuccess = success;
    ABBlock blockFailure = failure;
    

应该足够了。当 _success 块被复制时,它会依次复制成功和失败块。我什至不确定如果你__block在一个块上指定说明符会发生什么(也许它是一个无操作 - 将是一个很好的练习来找出),但没有必要这样做。

旁注:无需复制ABMessage *msg- 块将添加对其的强引用。

于 2013-05-02T09:10:10.673 回答
0

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

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

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

于 2013-05-02T11:35:38.937 回答