【问题标题】:AFNetworking Synchronous Operation in NSOperationQueue on iPhoneiPhone上NSOperationQueue中的AFNetworking同步操作
【发布时间】:2012-07-04 08:41:41
【问题描述】:

我的应用程序是这样工作的: - 创建相册并拍照 - 将它们发送到我的服务器上 - 图片分析后得到答案/补充信息。

发送部分有问题。这是代码

@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];
}

我的问题是:我的连接是异步建立的,因此即使在SyncAgent 中使用[self._queue setMaxConcurrentOperationCount:1],每个任务也会一起启动而无需等待前一个任务完成。

我需要同步执行每个连接吗?我认为这不是一个好主意,因为永远不应该以这种方式进行连接,而且我可能会在其他地方使用这些方法并且需要在后台执行它们,但我找不到更好的方法。有什么想法吗?

哦,如果我的代码中有任何错误/错字,我可以向您保证,当我在粘贴之前尝试对其进行总结时,它出现了,它现在可以正常工作。

谢谢!

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

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

【问题讨论】:

    标签: objective-c concurrency nsoperation nsoperationqueue afnetworking


    【解决方案1】:

    如果你对服务器有任何控制权,你真的应该考虑创建一个 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];
    

    【讨论】:

    • 不幸的是,这不起作用。启动批量上传的 NSOperation 仍然会在上传之前返回。我需要一种仅在上传完成/失败时返回的上传方法
    • 那我误解了你的要求。使用enqueueBatchOfHTTPRequestOperationsWithRequests:progressBlock:completionBlock:
    • 确实这可能是一种方法,但除非我创建一个在服务器上创建 ID 的方法,然后上传每个文件,否则它仍然不起作用。到目前为止,我只在需要时才使用我的 SyncTask 以正确的顺序执行它们,并且我不想在我的 Web 课程中这样做以保持它非常灵活。并且使用你的方式,我的 SyncAgent 仍然会“认为”每个 SyncTask (NSOperation) 已经完成。
    • 仍然找不到这样做的方法。我试过 -waitUntilFinished 但它似乎在等待连接超时,即使它成功了。奇怪的。有什么想法吗?
    • 一般情况下远离-waitUntilFinished。也许要记住的一件事是,当网络请求完成加载时,操作被标记为finished——无论如何,完成块中的任何内容都将在之后执行。
    【解决方案2】:

    以下代码对我有用,但我不确定它的缺点。用少许盐服用。

     - (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];
    
    }
    

    【讨论】:

    • 我必须承认我考虑过。这接缝有点hacky,但我想我还是会这样做。我正在考虑使用 dispatch_sync 来正确执行此操作,为我的所有上传设置一个队列,但这个解决方案更容易接缝。
    • 您应该将 NSCondition 替换为 GCD dispatch_groups。主要看dispatch_group_enterdispatch_group_waitdispatch_group_leave。 Mike Ash 有a good tutorial
    猜你喜欢
    • 2013-05-08
    • 1970-01-01
    • 1970-01-01
    • 2015-01-31
    • 2014-04-25
    • 2018-06-24
    • 1970-01-01
    • 2012-12-23
    • 1970-01-01
    相关资源
    最近更新 更多