[英]Parsing asynchronously using NSOperationQueue or GCD
我有这个解析操作,目前工作正常,但我已经开始注意到它稍微冻结了我的UI所以我试图重构并异步完成这个。 然而,我遇到了一些问题,希望有人能指出我正确的方向。 这是我当前的(同步)代码:
- (NSArray *)eventsFromJSON:(NSString *)objectNotation
{
NSParameterAssert(objectNotation != nil);
NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *eventsData = [NSJSONSerialization JSONObjectWithData:unicodeNotation options:0 error:&error];
if (eventsData == nil) {
//invalid JSON
return nil;
}
NSArray *events = [eventsData valueForKeyPath:@"resultsPage.results"];
if (events == nil) {
//parsing error
return nil;
}
NSLog(@"events looks like %@", events);
NSMutableArray *formattedEvents = [NSMutableArray arrayWithCapacity:events.count];
for (id object in [events valueForKeyPath:@"event"]) {
Event *event = [[Event alloc] init];
event.latitude = [object valueForKeyPath:@"location.lat"];
event.longitude = [object valueForKeyPath:@"location.lng"];
event.title = [object valueForKeyPath:@"displayName"];
event.venue = [object valueForKeyPath:@"venue.displayName"];
event.ticketsLink = [NSURL URLWithString:[object valueForKeyPath:@"uri"]];
event.artist = [object valueForKeyPath:@"performance.artist.displayName"];
event.date = [object valueForKeyPath:@"start.datetime"];
[formattedEvents addObject:event];
}
return [NSArray arrayWithArray:formattedEvents];
}
我一直在研究NSOperationQueue,我正在努力找到一个解决方案,因为我想从这个方法返回一个数组,操作队列并不意味着有返回值。 我也在看GCD,我有这样的想法:
- (NSArray *)eventsFromJSON:(NSString *)objectNotation
{
dispatch_queue_t backgroundQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__block NSMutableArray *mutable = [NSMutableArray array];
dispatch_async(backgroundQueue, ^{
NSParameterAssert(objectNotation != nil);
NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *eventsData = [NSJSONSerialization JSONObjectWithData:unicodeNotation options:0 error:&error];
if (eventsData == nil) {
//invalid JSON
mutable = nil;
}
NSArray *events = [eventsData valueForKeyPath:@"resultsPage.results"];
if (events == nil) {
//parsing error
mutable = nil;
}
NSLog(@"events looks like %@", events);
NSMutableArray *formattedEvents = [NSMutableArray arrayWithCapacity:events.count];
for (id object in [events valueForKeyPath:@"event"]) {
Event *event = [[Event alloc] init];
event.latitude = [object valueForKeyPath:@"location.lat"];
event.longitude = [object valueForKeyPath:@"location.lng"];
event.title = [object valueForKeyPath:@"displayName"];
event.venue = [object valueForKeyPath:@"venue.displayName"];
event.ticketsLink = [NSURL URLWithString:[object valueForKeyPath:@"uri"]];
event.artist = [object valueForKeyPath:@"performance.artist.displayName"];
event.date = [object valueForKeyPath:@"start.datetime"];
[formattedEvents addObject:event];
}
mutable = [NSMutableArray arrayWithArray:formattedEvents];
});
return [mutable copy];
}
出于某种原因,这似乎是在解析完成之前返回对象,但是因为我没有从该可变对象中获取数据,但我注意到解析确实正在发生(我正在注销结果)。 任何人都可以给我一个关于如何获得这个异步的东西的想法吗?
谢谢!!
您的主要问题是,它们的本质异步操作无法同步返回结果。 而不是从-eventsFromJSON:
返回数组,您应该为调用者提供一种在结果完成时接收回调的方法。 在Cocoa中有两种常见的方法。
您可以使用关联的委托协议创建委托,包括-parser:(Parser *)parser didFinishParsingEvents:(NSArray *)events
,然后让解析器在解析完成时在其委托上调用此方法。
另一种解决方案是允许调用者提供在解析完成时要执行的完成块。 所以,你可能会这样做:
- (void)eventsFromJSON:(NSString *)objectNotation completionHandler:(void (^)(NSArray *events))completionHandler)
{
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
NSMutableArray *mutable = [NSMutableArray array];
NSParameterAssert(objectNotation != nil);
NSData *unicodeNotation = [objectNotation dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
// Snip...
mutable = [NSMutableArray arrayWithArray:formattedEvents];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler([mutable copy]);
});
});
}
然后你可以这样调用这段代码:
- (void)parseJSONAndUpdateUI // Or whatever you're doing
{
NSString *jsonString = ...;
Parser *parser = [[Parser alloc] init];
[parser parseEventsFromJSON:jsonString completionHandler:^(NSArray *events){
// Update UI with parsed events here
}];
}
我更喜欢第二种基于块的方法。 在大多数情况下,它可以减少代码。 代码也更接近于同步方法,其中方法只返回一个数组,因为使用结果数组的代码只是跟随方法调用(虽然缩进,因为它在完成块的范围内)。
我建议使用你传递给你的解析方法的完成块。 这样,您不必返回值,但可以在解析后使用信息执行所需操作。 您只需要确保再次使用GCD将完成块放在主线程上。
一旦操作完成,您还可以在主线程上发布包含userInfo中的数组的通知。
但是,对于异步操作,返回值将不起作用。
在解析完成之前,您将获得返回的对象,因为您的返回[mutable copy]
位于dispatch_async块之外。 由于dispatch_async以异步方式运行,它将立即返回,然后调用return [mutable copy]
(由于未完成解析,因此为空)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.