我有一个表名称数组,希望循环遍历它们并获取它们的名称并附加一个URL来创建与Web服务的连接以下载JSON数据.循环似乎首先工作,数组中的三个表名中的每一个都被用于创建与Web服务的连接并且数据被下载但是当循环结束时(从3变为0)循环出现再次启动并为数组中的最后两个表无限循环.
记录输出(注意扬声器和参展商反复重复):
2013-12-16 10:38:08.755 WebServiceTest[501:60b] loopCount = 3 2013-12-16 10:38:08.758 WebServiceTest[501:60b] table name = workshop 2013-12-16 10:38:08.817 WebServiceTest[501:60b] LoopCount is: 2 2013-12-16 10:38:08.821 WebServiceTest[501:60b] loopCount = 2 2013-12-16 10:38:08.822 WebServiceTest[501:60b] table name = exhibitor 2013-12-16 10:38:08.827 WebServiceTest[501:60b] LoopCount is: 1 2013-12-16 10:38:08.830 WebServiceTest[501:60b] loopCount = 1 2013-12-16 10:38:08.831 WebServiceTest[501:60b] table name = speaker 2013-12-16 10:38:08.835 WebServiceTest[501:60b] LoopCount is: 0 2013-12-16 10:38:09.199 WebServiceTest[501:3707] Status code = 200 2013-12-16 10:38:09.204 WebServiceTest[501:3707] Objects in current table - speaker = 1 2013-12-16 10:38:09.207 WebServiceTest[501:3707] Status code = 200 2013-12-16 10:38:09.210 WebServiceTest[501:3707] Objects in current table - exhibitor = 2 2013-12-16 10:38:09.229 WebServiceTest[501:450b] Status code = 200 2013-12-16 10:38:09.234 WebServiceTest[501:450b] Objects in current table - speaker = 1 2013-12-16 10:38:09.240 WebServiceTest[501:3707] Status code = 200 2013-12-16 10:38:09.244 WebServiceTest[501:3707] Objects in current table - exhibitor = 2 2013-12-16 10:38:09.271 WebServiceTest[501:450b] Status code = 200 2013-12-16 10:38:09.274 WebServiceTest[501:450b] Objects in current table - speaker = 1 2013-12-16 10:38:09.294 WebServiceTest[501:450b] Status code = 200 2013-12-16 10:38:09.298 WebServiceTest[501:4803] Status code = 200 2013-12-16 10:38:09.302 WebServiceTest[501:4803] Objects in current table - exhibitor = 2 2013-12-16 10:38:09.309 WebServiceTest[501:4803] Status code = 200 2013-12-16 10:38:09.337 WebServiceTest[501:4803] Objects in current table - speaker = 1 2013-12-16 10:38:09.338 WebServiceTest[501:4803] Status code = 200 2013-12-16 10:38:09.341 WebServiceTest[501:4803] Objects in current table - exhibitor = 2 2013-12-16 10:38:09.347 WebServiceTest[501:4803] Status code = 200 2013-12-16 10:38:09.352 WebServiceTest[501:4803] Objects in current table - speaker = 1 2013-12-16 10:38:09.311 WebServiceTest[501:450b] Objects in current table - workshop = 143
viewController.h:
#import@interface ViewController : UIViewController { NSMutableArray *arrayTable; } @property (weak, nonatomic) IBOutlet UITableView *myTableView; @property NSMutableArray *tables; @property NSMutableArray *tableNames; @property NSMutableURLRequest *request; @property NSString *tableName; -(void) startUpdate; typedef void(^completion_t)(NSArray* objects, NSError*error); -(void)fetchData:(NSString *)tableName withCompletion:(completion_t)completionHandler; -(void)fetchObjectsWithTableName:(NSString*)tableName completion:(completion_t)completionHandler;
viewController.m:
#import "ViewController.h" @implementation ViewController - (void)viewDidLoad { [self startUpdate]; [super viewDidLoad]; [[self myTableView]setDelegate:self]; [[self myTableView]setDataSource:self]; arrayTable =[[NSMutableArray alloc]init]; } -(void)startUpdate { NSArray* tableNames = @[@"speaker", @"exhibitor", @"workshop"]; NSUInteger loopCount = tableNames.count; while (loopCount > 0){ NSString *tableName = [tableNames objectAtIndex:loopCount -1]; NSLog(@"loopCount = %lu", (unsigned long)loopCount); NSLog(@"table name = %@", tableName); [self fetchObjectsWithTableName:[tableName mutableCopy] completion:^(NSArray* objects, NSError*error){ if (error) { NSLog(@"Error: %@", error); } else { NSLog(@"Result: %@", objects); } }]; loopCount --; NSLog(@"LoopCount is: %i", loopCount); } } -(void)fetchObjectsWithTableName:(NSString*)tableName completion:(completion_t)completionHandler; { [self fetchData:tableName withCompletion:^(NSArray* objects, NSError*error){ if (objects) { [self.tables addObject:objects]; [self fetchObjectsWithTableName:tableName completion:completionHandler]; } else if (objects == nil){ NSLog(@"objects = %@", objects); NSLog(@"break out of FOWTN"); } else { NSLog(@"Objects is something else..."); } }]; if (completionHandler) { completionHandler(self.tables, nil); } } -(void)fetchData:(NSString *)tableName withCompletion:(completion_t)completionHandler { NSString *currentURL = [NSString stringWithFormat:@"https://testapi.someURL.com/api/congress/%@", tableName]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:currentURL]]; [request addValue:@"application/json" forHTTPHeaderField:(@"Accept")]; [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { NSError* err = error; NSArray* objects; // final result array as a representation of JSON Array if (response) { NSHTTPURLResponse *newResp = (NSHTTPURLResponse*)response; if (newResp.statusCode == 200) { NSLog(@"Status code = %li", (long)newResp.statusCode); if ([data length] >0 && error == nil) { NSError* localError; objects = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; if (objects) { if (completionHandler) { completionHandler(objects, nil); } //NSLog(@"Objects in current table - %@ = %@", tableName, objects); NSLog(@"Objects in current table - %@ = %lu", tableName, (unsigned long)objects.count); return; } else { err = localError; } } else { // err = ... } } } if (objects == nil) { NSLog(@"Nothing found in table: %@", tableName); //assert(err); if (completionHandler) { completionHandler(nil, err); } } }]; }
CouchDevelop.. 8
(编辑:已删除)
恕我直言,循环看起来更好用for循环,在"右"方向迭代:
-(void)startUpdate { NSUInteger count = tableNames.count; for (int i = 0; i < count; ++i){ NSString *tableName = [tableNames objectAtIndex:i]; ... } }一些额外的建议:
说完这个并修复了这个问题,现在,你需要意识到你正在循环块中调用异步方法.因此,你的startUpdate
方法变成自己异步的!
如果您希望在完成所有异步方法后通知调用站点,您startUpdate
可以使用完成处理程序:
- (void) startUpdateWithCompletion:(completion_t)completionHandler;
您可以在a中调用异步方法for loop
.但实际上,这将并行处理所有异步任务,除非底层异步任务通过在"大小"(同时操作数)可配置的专用共享队列上执行来自行处理.
现在,一个合适的具体实现取决于您的要求,即您是否需要控制同时运行的任务的数量以及您希望在何处完成此任务.它还取决于您是否希望能够在任何时候从任何其他线程取消循环,如果这是必要的话.
假设,我们不对底层异步任务做任何假设,这意味着它们不会使用共享专用队列来限制同时运行的任务本身的数量.此外,您希望确保所有任务一个接一个地运行- 或串行运行.
一种方法(几种方法)是使用a NSOperationQueue
,将其最大操作数设置为1,并添加需要包装成"并发"的异步任务NSOperation
.
"并发" NSOperation
是需要以该start
方法启动的操作,该方法是异步的.基本上,当您的异步任务完成时,操作将完成.添加到a时NSOperationQueue
,队列会在何时开始操作.
不幸的是,将NSOperation
a 子类化为并发操作需要注意一些细微之处.官方文档Subclassing Notes没有详细描述它们,因此您可以在这里看一下这段代码片段:NSOperation子类的规范实现.
现在,假设您有一个正确的a子类NSOperation
,请调用它FetchTableOperation
:
completion_t completionHandler = ^(..) {..}; NSOperationQueue* queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperations = 1; for (NSString tableName in self.tableNames) { FetchTableOperation* op = [[FetchTableOperation alloc] initWithName:tableName completion: ^{...}]; [queue addOperation:op]; }
为了在操作完成时得到通知,添加"sentinel"块:
[queue addOperationWithBlock:^{ // finished }];
警告:
您需要创建一个适当的并发 子类NSOperation
来包装您的异步方法.
上次操作完成后会收到通知,而不是上次操作完成块完成时的通知!
完成处理程序仍然可以并行执行!(除非它们在主线程或大小等于1 的专用队列上执行)
除非任务数量非常大,否则所有任务都将排队 - 这不是问题.每个排队的任务都会消耗一点系统RAM.
使用a的优点NSOperationQueue
是您可以随时取消挂起的操作.此外,您可以使用属性轻松调整队列的大小,即最大并发操作的数量maxConcurrentOperations
.
相比之下,这是一种快速简便的解决方案.但是,您的任务将并行启动:
dispatch_group group = dispatch_group_create(); for (NSString* tableName in self.tableNames) { dispatch_group_enter(group); [self fetchObjectsWithTableName:tableName completion:^{ ... dispatch_group_leave(group); }]; } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ ... // all tasks *and* all completion handler finished });
这也是一个非常简单的解决方案 - 一旦你理解了模式.
如何在iOS中异步下载多个图像而不影响UI?
NSArray
顺序调用异步任务的类别:这是一个"可重用"组件,一旦实现,就可以轻松使用.您可以按如下方式使用它:
typedef void (^unary_async_t)(id input, completion_t completion); typedef void (^completion_t)(id result); unary_async_t task = ^(id input, completion_t completionHandler) { [self fetchObjectsWithTableName:input completion:^(NSData* result, NSError*error){ if (error == nil) { ... ; } completionHandler(error ? error : @"OK"); }]; }; NSArray* tableNames = @[@"speaker", @"exhibitor", @"workshop"]; [tableNames forEachApplyTask:task completion:^(id result){ // result is an array containing the result of each operation in the same order ... }];
https://gist.github.com/couchdeveloper/6155227
(编辑:已删除)
恕我直言,循环看起来更好用for循环,在"右"方向迭代:
-(void)startUpdate { NSUInteger count = tableNames.count; for (int i = 0; i < count; ++i){ NSString *tableName = [tableNames objectAtIndex:i]; ... } }
说完这个并修复了这个问题,现在,你需要意识到你正在循环块中调用异步方法.因此,你的startUpdate
方法变成自己异步的!
如果您希望在完成所有异步方法后通知调用站点,您startUpdate
可以使用完成处理程序:
- (void) startUpdateWithCompletion:(completion_t)completionHandler;
您可以在a中调用异步方法for loop
.但实际上,这将并行处理所有异步任务,除非底层异步任务通过在"大小"(同时操作数)可配置的专用共享队列上执行来自行处理.
现在,一个合适的具体实现取决于您的要求,即您是否需要控制同时运行的任务的数量以及您希望在何处完成此任务.它还取决于您是否希望能够在任何时候从任何其他线程取消循环,如果这是必要的话.
假设,我们不对底层异步任务做任何假设,这意味着它们不会使用共享专用队列来限制同时运行的任务本身的数量.此外,您希望确保所有任务一个接一个地运行- 或串行运行.
一种方法(几种方法)是使用a NSOperationQueue
,将其最大操作数设置为1,并添加需要包装成"并发"的异步任务NSOperation
.
"并发" NSOperation
是需要以该start
方法启动的操作,该方法是异步的.基本上,当您的异步任务完成时,操作将完成.添加到a时NSOperationQueue
,队列会在何时开始操作.
不幸的是,将NSOperation
a 子类化为并发操作需要注意一些细微之处.官方文档Subclassing Notes没有详细描述它们,因此您可以在这里看一下这段代码片段:NSOperation子类的规范实现.
现在,假设您有一个正确的a子类NSOperation
,请调用它FetchTableOperation
:
completion_t completionHandler = ^(..) {..}; NSOperationQueue* queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperations = 1; for (NSString tableName in self.tableNames) { FetchTableOperation* op = [[FetchTableOperation alloc] initWithName:tableName completion: ^{...}]; [queue addOperation:op]; }
为了在操作完成时得到通知,添加"sentinel"块:
[queue addOperationWithBlock:^{ // finished }];
警告:
您需要创建一个适当的并发 子类NSOperation
来包装您的异步方法.
上次操作完成后会收到通知,而不是上次操作完成块完成时的通知!
完成处理程序仍然可以并行执行!(除非它们在主线程或大小等于1 的专用队列上执行)
除非任务数量非常大,否则所有任务都将排队 - 这不是问题.每个排队的任务都会消耗一点系统RAM.
使用a的优点NSOperationQueue
是您可以随时取消挂起的操作.此外,您可以使用属性轻松调整队列的大小,即最大并发操作的数量maxConcurrentOperations
.
相比之下,这是一种快速简便的解决方案.但是,您的任务将并行启动:
dispatch_group group = dispatch_group_create(); for (NSString* tableName in self.tableNames) { dispatch_group_enter(group); [self fetchObjectsWithTableName:tableName completion:^{ ... dispatch_group_leave(group); }]; } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ ... // all tasks *and* all completion handler finished });
这也是一个非常简单的解决方案 - 一旦你理解了模式.
如何在iOS中异步下载多个图像而不影响UI?
NSArray
顺序调用异步任务的类别:这是一个"可重用"组件,一旦实现,就可以轻松使用.您可以按如下方式使用它:
typedef void (^unary_async_t)(id input, completion_t completion); typedef void (^completion_t)(id result); unary_async_t task = ^(id input, completion_t completionHandler) { [self fetchObjectsWithTableName:input completion:^(NSData* result, NSError*error){ if (error == nil) { ... ; } completionHandler(error ? error : @"OK"); }]; }; NSArray* tableNames = @[@"speaker", @"exhibitor", @"workshop"]; [tableNames forEachApplyTask:task completion:^(id result){ // result is an array containing the result of each operation in the same order ... }];
https://gist.github.com/couchdeveloper/6155227
你在while
循环中调用块操作.操作无法完成(可能还没有完成.你应该在这里使用重复.我用pesudocode写它:
你有一个数组与一些项目连接.
您在单独的方法中激活块操作 fetchWith:
在complete
块之后,如果index + 1仍然出现在数组中,则会触发相同的方法.
如果您逐个触发连接,请离开该方法
使用这种方法,您只需触发一次方法,然后重复处理其余的方法.
如果您想同时触发多个连接,您可能应该考虑委托.
编辑: 好的,这是你如何做到这一点:
-(void)fetchNowWithIndex:(NSInteger)index { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *downloadData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[@"yourURL"]]; dispatch_async(dispatch_get_main_queue(), ^{ //refresh label here [self.arrayWithLabelsToChange replaceObjectAtIndex:index]; [self fetchNowWithIndex:index+1] }); }); }