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