1

我的应用程序以这种方式工作: - 创建相册和拍照 - 送他们我的服务器上 - 让图片分析后的答案/补充信息。AFNetworking同步操作在NSOperationQueue iPhone上

我有一些问题与发送部分。下面是代码

@interface SyncAgent : NSObject <SyncTaskDelegate> 

@property NSOperationQueue* _queue; 
-(void)launchTasks; 

@end 


@implementation SyncAgent 

@synthesize _queue; 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     self._queue = [[NSOperationQueue alloc] init]; 
     [self._queue setMaxConcurrentOperationCount:1]; 
    } 
    return self; 
} 

-(void) launchTasks { 

    NSMutableArray *tasks = [DataBase getPendingTasks]; 

    for(Task *t in tasks) { 
     [self._queue addOperation:[[SyncTask alloc] initWithTask:t]]; 
    } 
} 
@end 

和SyncTask:

@interface SyncTask : NSOperation 

@property (strong, atomic) Task *_task; 
-(id)initWithTask:(Task *)task; 
-(void)mainNewID; 
-(void)mainUploadNextPhoto:(NSNumber*)photoSetID; 

@end 

@implementation SyncTask 

@synthesize _task; 

-(id)initWithTask:(Task *)task { 
    if(self = [super init]) { 
     self._task = task; 
    } 
    return self; 
} 

-(void)main { 

    NSLog(@"Starting task : %@", [self._task description]); 
    // checking if everything is ready, sending delegates a message etc 

    [self mainNewID]; 
} 

-(void)mainNewID { 

    __block SyncTask *safeSelf = self; 

    [[WebAPI sharedClient] createNewPhotoSet withErrorBlock:^{ 
     NSLog(@"PhotoSet creation : error") 
    } andSuccessBlock:^(NSNumber *photoSetID) { 
     NSLog(@"Photoset creation : id is %d", [photoSetID intValue]); 
     [safeSelf mainUploadNextPhoto:photoSetID]; 
    }]; 
} 

-(void)mainUploadNextPhoto:(NSNumber*) photoSetID { 

    //just admit we have it. won't explain here how it's done 
    NSString *photoPath; 

    __block SyncTask *safeSelf = self; 

    [[WebAPI sharedClient] uploadToPhotosetID:photoSetID withPhotoPath:photoPath andErrorBlock:^(NSString *photoPath) { 
     NSLog(@"Photo upload error : %@", photoPath); 

    } andSuccessBlock:^(NSString *photoPath) { 

     NSLog(@"Photo upload ok : %@", photoPath); 
     //then we delete the file 
     [safeSelf mainUploadNextPhoto:photoSetID]; 
    }]; 
} 
@end 

每个网络操作使用AFNetworking这样做的话:

-(void)myDummyDownload:(void (^)(NSData * data))successBlock 
{ 
    AFHTTPClient* _httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:@"http://www.google.com/"]]; 
    [_httpClient registerHTTPOperationClass:[AFHTTPRequestOperation class]]; 

    NSMutableURLRequest *request = [_httpClient requestWithMethod:@"GET" path:@"/" nil]; 
    [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; 

    AFHTTPRequestOperation *operation = [_httpClient HTTPRequestOperationWithRequest:(NSURLRequest *)request 
     success:^(AFHTTPRequestOperation *operation, id data) { 
      if(dataBlock) 
       dataBlock(data); 
     } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
      NSLog(@"Cannot download : %@", error); 
    }]; 

    [operation setShouldExecuteAsBackgroundTaskWithExpirationHandler:^{ 
     NSLog(@"Request time out"); 
    }]; 

    [_httpClient enqueueHTTPRequestOperation:operation]; 
} 

我的问题是:我的连接是异步的,所以每个任务都会一起启动,无需等到[self._queue setMaxConcurrentOperationCount:1]SyncAgent即可完成。

我是否需要同步执行每个连接?我不认为这是一个好主意,因为连接永远不应该这样做,也因为我可能会在别处使用这些方法,并且需要它们在后台执行,但是我找不到更好的方法。任何想法 ?

哦,如果在我的代码中有任何错误/拼写错误,我可以向你保证,当我在粘贴它之前总结它时,它现在没有任何问题。

谢谢!

PS:很抱歉,我找不到更好的解决问题的方法。

编辑:我发现,使用信号量更容易建立和理解:How do I wait for an asynchronously dispatched block to finish?

回答

1

以下代码适用于我,但我不确定是否有缺点。拿一点盐就可以了。

- (void) main { 

    NSCondition* condition = [[NSCondition alloc] init]; 
    __block bool hasData = false; 
    [condition lock]; 

    [[WebAPI sharedClient] postPath:@"url" 
         parameters:queryParams 
          success:^(AFHTTPRequestOperation *operation, id JSON) { 
          //success code 
          [condition lock]; 
          hasData = true; 
          [condition signal]; 
          [condition unlock]; 

          } 
          failure:^(AFHTTPRequestOperation *operation, NSError *error) { 

          //failure code 

          [condition lock]; 
          hasData = true; 
          [condition signal]; 
          [condition unlock]; 
          }]; 

    while (!hasData) { 
    [condition wait]; 
    } 
    [condition unlock]; 

} 
+0

我必须承认我想到了。这接近了一些不好意思,但我想我会做到这一点。我正在考虑使用'dispatch_sync'正确执行,为我所有的上传文件设置一个队列,但这个解决方案更容易。 – dvkch

+1

您应该用GCD dispatch_groups替换NSCondition。主要看'dispatch_group_enter','dispatch_group_wait'和'dispatch_group_leave'。迈克阿什有[一个很好的教程](http://www.mikeash.com/pyblog/friday-qa-2009-09-04-intro-to-grand-central-dispatch-part-ii-multi-core-performance的.html)。 –

2

如果你有对服务器的任何控制可言,你真的应该考虑创建一个API,允许你上传照片以任意的顺序,以支持多个同时上传(对于大批量,这可以相当快)。

但是,如果你必须做同步的事情,最简单的方法可能是排队在请求的完成块的新请求。即

// If [operations length] == 1, just enqueue it and skip all of this 
NSEnumerator *enumerator = [operations reverseObjectEnumerator]; 
AFHTTPRequestOperation *currentOperation = nil; 
AFHTTPRequestOperation *nextOperation = [enumerator nextObject]; 
while (nextOperation != nil && (currentOperation = [enumerator nextObject])) { 
    currentOperation.completionBlock = ^{ 
    [client enqueueHTTPRequestOperation:nextOperation]; 
    } 
    nextOperation = currentOperation; 
} 
[client enqueueHTTPRequestOperation:currentOperation]; 
+0

不幸的是,这是行不通的。启动批量上传的NSOperation在上传之前仍会返回。我需要一个上传方法,只有在上传完成/失败时才会返回 – dvkch

+0

然后我误解了你的要求。使用'enqueueBatchOfHTTPRequestOperationsWithRequests:progressBlock:completionBlock:' – mattt

+0

实际上这可能是一种方式,但它仍然不起作用,除非我创建一个方法在服务器上创建ID,然后上传每个文件。到目前为止,我只是在需要的时候才使用我的SyncTask来按正确的顺序执行它们,并且我宁愿不要在我的Web课程中使它保持非常灵活。并用你的方式我的SyncAgent仍然会“认为”每一个SyncTask(NSOperation)都已经完成。 – dvkch