Ios objective-c中的循环和异步连接
我有一个表名数组,希望遍历它们,获取它们的名称并附加一个URL来创建到web服务的连接,以下载JSON数据。循环一开始似乎有效,数组中的三个表名中的每一个都被获取并用于创建到web服务的连接,数据被下载,但当循环完成(从3到0)时,循环似乎再次启动,并无限循环数组中的最后两个表 日志输出(请注意,演讲者和参展商会反复出现): viewController.h:Ios objective-c中的循环和异步连接,ios,objective-c,cocoa-touch,loops,asynchronous,Ios,Objective C,Cocoa Touch,Loops,Asynchronous,我有一个表名数组,希望遍历它们,获取它们的名称并附加一个URL来创建到web服务的连接,以下载JSON数据。循环一开始似乎有效,数组中的三个表名中的每一个都被获取并用于创建到web服务的连接,数据被下载,但当循环完成(从3到0)时,循环似乎再次启动,并无限循环数组中的最后两个表 日志输出(请注意,演讲者和参展商会反复出现): viewController.h: #import <UIKit/UIKit.h> @interface ViewController : UIViewCon
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController<UITableViewDataSource, UITableViewDelegate, NSURLConnectionDataDelegate>{
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;
您正在
内部调用块操作,而循环。操作无法完成(可能尚未完成。您应该在此处使用recurrence。我用伪代码编写:
您有一个数组,其中包含要连接的某些项
您可以使用名为fetchWith:
在complete
after block中,如果索引+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]
});
});
}
(编辑:已删除)
IMHO,使用for循环时,循环看起来更好,在“正确”方向迭代:
-(void)startUpdate
{
NSUInteger count = tableNames.count;
for (int i = 0; i < count; ++i){
NSString *tableName = [tableNames objectAtIndex:i];
...
}
}
您可以在for循环
中调用异步方法。但实际上,这将并行处理所有异步任务,除非底层异步任务通过在“大小”(同时操作的数量)可配置的私有共享队列上执行来自行处理
现在,一个合适的具体实现取决于您的需求,即您是否需要控制同时运行的任务的数量以及您希望在何处完成此任务。它还取决于您是否希望能够在任何时候从任何其他线程取消循环(如果有必要)
例如:
假设,我们不对底层异步任务进行任何假设,这意味着它们不会使用共享专用队列来限制同时运行的任务的数量。此外,您希望确保所有任务逐个或串行运行
一种方法(几种方法中的一种)是使用NSOperationQueue
,将其最大操作数设置为1,并添加需要包装到“并发”NSOperation
中的异步任务
“并发”NSOperation
是一种需要使用异步的start
方法启动的操作。基本上,该操作将在异步任务完成时完成。添加到NSOperationQueue
时,队列会注意何时启动操作
不幸的是,将NSOperation
子类化为并发操作需要注意一些细节。官方文档没有详细描述这些细节,因此您可以在此处查看此代码片段:
现在,假设您有一个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”块:
注意事项:
- 您需要创建并发
NSOperation
的适当子类来包装异步方法
- 您将在最后一个操作完成时收到通知,而不是在最后一个操作的完成块完成时收到通知
- 完成处理程序仍然可以并行执行!(除非它们在主线程或大小等于1的私有队列上执行)
- 所有的任务都将被排队——这不是问题,除非任务的数量真的很大。每个排队的任务只会消耗一点系统RAM
使用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
});
按顺序运行异步任务的“异步循环”:
这也是一个非常简单的解决方案——一旦您理解了模式
使用顺序调用异步任务的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
...
}];
我仍然不明白“fetchData”是如何反复运行的,并且只针对传递给它的三个参数中的两个。我希望loopCount达到0,循环停止运行。“fetchObjectsWithTableName”和“fetchData”在每个循环中只调用一次。抱歉,我没有深入研究您的代码,因为它很简单对于你来说,这很复杂。这是我想到的例子。@Ryan这种方法与我称之为“异步循环”的方法相同。事实上,它不是递归-它是迭代的。这是“标准模式”之一对于异步问题。为了隐藏复杂性,您可以将代码放在某个助手方法中-如我的答案中代码片段的链接所示。+1.再次感谢您提供了一个全面的答案,这无疑是一个“学童错误”类型的问题。我现在将研究您的建议。哦,循环与在e编码,因为计数是3,但索引数组是0,1,2,而不是1,2,3,所以计数-1是3=2,2=1,1=0。我将把你的答案标记为a
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];
}
[queue addOperationWithBlock:^{
// finished
}];
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
});
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
...
}];