使用 AFNetworking 从多个资源获取数据并返回



我正在尝试在 ObjectC 中编写一个函数,该函数返回一个具有 3 个属性的类。对于程序必须从远程服务器获取的每个属性(AFNetworking),我想知道这个问题的好解决方案是什么?这是我正在编写的函数的当前结构

- (MyClass *)fillInClassAndReturn {
    MyClass *myClass = [MyClass new];
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    [manager GET:@"Url1"
      parameters:parameter1
         success:^(AFHTTPRequestOperation *operation, id responseObject) {
             myClass.property1 = responseObject;
         } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
             myClass.property1 = nil;
         }];
    [manager GET:@"Url2"
      parameters:parameter2
         success:^(AFHTTPRequestOperation *operation, id responseObject) {
             myClass.property2 = responseObject;
         } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
             myClass.property2 = nil;
         }];
    [manager GET:@"Url13"
      parameters:parameter3
         success:^(AFHTTPRequestOperation *operation, id responseObject) {
             myClass.property3 = responseObject;
         } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
             myClass.property3 = nil;
         }];
    return myClass;
}
此函数

将在调用此函数后立即返回myClass,此时将nil所有三个属性,当从服务器获取相应的数据时,将填充它。因此,使用此函数的人必须为函数的返回值定义 KVO,并在值更改时收到通知。但是这是这个设计的一个问题,如果返回的值是NSArray/NSMutableArray而不是MyClass,它将不起作用那么有没有更好的设计,这样我就可以处理这种情况?如果这个设计没有那么可怕,如何处理NSArray*的情况?

几个观察结果:

  1. 由于这是一个异步运行的方法,因此您不希望尝试立即返回任何内容,而是使用完成块模式,即在三个请求完成时将调用的completionHandler

  2. 由于您有三个同时运行的请求,因此您需要某种方法来了解所有三个请求何时完成。由于使用的是基于 NSOperation 的解决方案 ( AFHTTPRequestOperation ),因此可以使用操作依赖项。

无论如何,这会产生如下结果:

- (void)fillInClassWithCompletion:(void (^)(MyClass *myClass))completionHandler {
    MyClass *myClass = [MyClass new];
    NSBlockOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
        if (completionHandler) {
            completionHandler(myClass);
        }
    }];
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    NSOperation *operation1 = [manager GET:@"Url1" parameters:parameter1 success:^(AFHTTPRequestOperation *operation, id responseObject) {
        myClass.property1 = responseObject;
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        myClass.property1 = nil;
    }];
    [completionOperation addDependency:operation1];
    NSOperation *operation2 = [manager GET:@"Url2" parameters:parameter2 success:^(AFHTTPRequestOperation *operation, id responseObject) {
        myClass.property2 = responseObject;
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        myClass.property2 = nil;
    }];
    [completionOperation addDependency:operation2];
    NSOperation *operation3 = [manager GET:@"Url13" parameters:parameter3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
        myClass.property3 = responseObject;
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        myClass.property3 = nil;
    }];
    [completionOperation addDependency:operation3];
    [[NSOperationQueue mainQueue] addOperation:completionOperation];
}

这可能是最合乎逻辑的方法。从技术上讲,如果将AFHTTPRequestOperationManagercompletionQueue更改为并发队列,则上述内容引入了可能的争用条件,因此您可以使用调度组机制来了解是否完成了所有三个:

- (void)fillInClassWithCompletion:(void (^)(MyClass *myClass))completionHandler {
    MyClass *myClass = [MyClass new];
    dispatch_group_t group = dispatch_group_create();
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    dispatch_group_enter(group);
    [manager GET:@"Url1" parameters:parameter1 success:^(AFHTTPRequestOperation *operation, id responseObject) {
        myClass.property1 = responseObject;
        dispatch_group_leave(group);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        myClass.property1 = nil;
        dispatch_group_leave(group);
    }];
    dispatch_group_enter(group);
    [manager GET:@"Url2" parameters:parameter2 success:^(AFHTTPRequestOperation *operation, id responseObject) {
        myClass.property2 = responseObject;
        dispatch_group_leave(group);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        myClass.property2 = nil;
        dispatch_group_leave(group);
    }];
    dispatch_group_enter(group);
    [manager GET:@"Url13" parameters:parameter3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
        myClass.property3 = responseObject;
        dispatch_group_leave(group);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        myClass.property3 = nil;
        dispatch_group_leave(group);
    }];
    dispatch_group_notify(group, manager.completionQueue ?: dispatch_get_main_queue(), ^{
        if (completionHandler) {
            completionHandler(myClass);
        }
    });
}

无论哪种方式,您使用它的方式都是:

[object fillInClassWithCompletion:^(MyClass *myClass) {
    // use myClass here
}];

您可能还希望扩展此completionHandler以传回错误对象,因为这是常见做法,如果调用方希望根据错误的性质自定义其错误处理,则此方法很有用,但我会将其留给您。

最新更新