我有一些网络代码,正在进行大量的JSON解析。它需要在后台完成,以阻止主线程。代码如下所示:
-(void) getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
NSURLRequest *req = ...
AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];
// sometimes I have more requests
// startOperations is a wrapper on AFHTTPClient enqueueBatchOfHTTPRequestOperations:progressBlock:completionBlock:
// that handles errors and loading views
[self startOperations:@[op] completionBlock:^(NSArray *operations) {
// getBgQueue = return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(getBgQueue(), ^{
NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
[self.localContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) {
// this is executed on main thread
if(completion) completion(...);
}];
});
}];
}
(AFNetworking 1.x)
上面的代码工作得非常好,但是设置和编写都很痛苦。通常整个方法内容都包含在另一个块中,以便首先获取一些所需的数据...基本上这些块只是堆积起来并制作丑陋的代码
我正在使用enqueueBatchOfHTTPRequestOperations
而不是AFJSONRequestOperation
上的单个完成块,因为批处理完成块有时会在所有单个操作完成块之前触发...(我也读过Mattt不鼓励这样做的地方)
有关如何做得比这更好的指示?
答案 0 :(得分:1)
我不确定你想要什么,但就像“longcat很长”一样,它在模式中有些固有:'延续传递风格是延续传递风格'。如果你想稍微讨论一下,你可以制作局部块变量,但是在某种程度上,你因为-MR_saveToPersistentStoreWithCompletion
需要完成才能关闭data
以便通过而被卡住了它已完成-getSomeDataWithParameters...
,但在执行data
完成之前,-startOperations
将不存在。
你可以通过使用一堆__block
变量,并将代码分成几个本地块来实现较少嵌套的外观,但对我来说,感觉有点像削减你的鼻子来惹恼你的脸。这段代码很容易理解。
顺便说一下......我注意到你在op
完成区块中关闭了-startOperations
。这很好,因为你通过op
加入-startOperations: @[op] ...
,但从op
参数到完成operations
可能会更清晰。我尽可能合理地收紧了这个:
- (void)getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
NSURLRequest *req = ...;
AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];
[self startOperations:@[op] completionBlock:^(NSArray *operations) {
for (AFJSONRequestOperation *op in operations) {
dispatch_async(getBgQueue(), ^{
NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
void (^mrSaveCompletion)(BOOL, NSError*) = completion ? ^(BOOL success, NSError *error) { completion(data); } : nil;
[self.localContext MR_saveToPersistentStoreWithCompletion: mrSaveCompletion];
});
}
}];
}
这会将每个响应分散到不同的线程。如果您希望在单个后台线程上执行所有响应,只需交换for
循环和dispatch_async
的嵌套。
从那里,唯一真正“多余”的代码是dispatch_async
。您可以通过使-startOperations:...
获取队列参数来消除这种情况,您可以在队列中传入您希望调用完成的队列。也许是这样的:
- (void)startOperations: (NSArray*)ops completionQueue: (dispatch_queue_t)queue completionBlock: (void (^)(NSArray*))completion
{
void (^completionWrapper)(NSArray*) = !completion ? nil : ^(NSArray* ops) {
if (queue)
dispatch_async(queue, ^{ completion(ops); });
else
completion(ops);
};
[self startOperations: ops completionBlock: completionWrapper];
}
- (void)getSomeDataWithParameters:(...)parameters completion:(void (^)(NSArray *data))completion
{
NSURLRequest *req = ...;
AFJSONRequestOperation *op = [[AFJSONRequestOperation alloc] initWithRequest:req];
[self startOperations:@[op] completionQueue: getBgQueue() completionBlock:^(NSArray *operations) {
for (AFJSONRequestOperation *op in operations) {
NSArray *data = [MyParserClass parseJSON:op.responseJSON inContext:self.localContext];
void (^mrSaveCompletion)(BOOL, NSError*) = !completion ? nil : ^(BOOL success, NSError *error) { completion(data); };
[self.localContext MR_saveToPersistentStoreWithCompletion: mrSaveCompletion];
});
}];
}