这次崩溃是相当罕见的,但它经常发生,足以说服我,我做错了。
这是在自定义并发队列上使用主线程异步调度和屏障调度执行的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)。
答案 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
数组的值,你会怎么做?)
您不需要在success
和failure
块上指定__block:
ABBlock blockSuccess = success;
ABBlock blockFailure = failure;
应该足够了。复制_success块后,它将依次复制成功和失败块。我甚至不确定如果你在一个区块上指定__block
说明符会发生什么(也许它是一个很好的练习 - 这是一个很好的练习),但是没有需要它。
附注:无需复制ABMessage *msg
- 块会添加强引用。
答案 2 :(得分:0)
尝试通过手动执行以下操作将此块复制到堆中:
[[ABClient sharedInstance] events:[_success copy] failure:nil];
由于您在另一个success
中调用dispatch_async
,因此必须在发送之前将您在+fetch
中声明的块复制到堆中。