AFNetworking 回调 - 委托还是通知?

Posted

技术标签:

【中文标题】AFNetworking 回调 - 委托还是通知?【英文标题】:AFNetworking callbacks - delegate or notification? 【发布时间】:2015-03-17 15:15:52 【问题描述】:

我对将 AFNetworking 结果发送到控制器的最佳方式或正确方式有疑问。是通过委托还是通知?

我创建了一个类来处理具有以下代码的 API 调用。因此,如果将此类导入另一个控制器并调用此方法进行 API 调用。我应该委托还是通知?

我已阅读 www.raywenderlich.com/59255/afnetworking-2-0-tutorial,它正在使用委托。我还观看了 CodeSchool 教程,他们使用了从模型到控制器的通知。

我添加了下面的代码,希望能更好地展示我的问题。

AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:baseURL];

// notification way inside the BLOCK
[ manager GET:path parameters:params
     success:^(NSURLSessionDataTask *operation, id responseObject) 
         [ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
                                                              object:nil
                                                            userInfo:responseObject ];

      failure:^(NSURLSessionDataTask *operation, NSError *error) 
         [ [NSNotificationCenter defaultCenter] postNotificationName:notificationName
                                                              object:nil ];
     ];

// delegate way inside the BLOCK
[ manager GET:path parameters:params
     success:^(NSURLSessionDataTask *operation, id responseObject) 

       if ([delegate respondsToSelector:@selector(getUserFeedsDidFinish:resultDict:)])
       
            [delegate performSelector:@selector(getUserFeedsDidFinish:resultDict:) withObject:self withObject:resultDict];
       

      failure:^(NSURLSessionDataTask *operation, NSError *error) 
       if ([delegate respondsToSelector:@selector(getUserFeeds:didFailWithResultDict:)]) 
           [delegate performSelector:@selector(getUserFeeds:didFailWithResultDict:)
                          withObject:self
                          withObject:[NSDictionary dictionaryWithObject:error.userInfo forKey:KEY_ERRORS]];
        
     ];

【问题讨论】:

【参考方案1】:

我会推荐使用积木,如何?我给你写一个服务,这个写在一个叫Connection的类里:

+(void)requestLocation:(NSString*)googleReference completionBlock:(void (^)(NSString * coordinates, NSError * error)) handler

NSString * urlString = @"https://maps.googleapis.com/maps/";
NSMutableDictionary * parametersDictionary = [NSMutableDictionary dictionary];
[parametersDictionary setObject:googleReference forKey:@"reference"];
[parametersDictionary setObject:@"true" forKey:@"sensor"];
[parametersDictionary setObject:@"key(it is not)" forKey:@"key"];

AFHTTPClient *HTTPClient = [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:urlString]];
NSURLRequest *URLRequest = [HTTPClient requestWithMethod:@"GET" path:@"api/place/details/json" parameters:parametersDictionary];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:URLRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) 
    NSError * error = nil;
    NSDictionary * response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:&error];
    NSDictionary * dicGeo = [((NSDictionary*)[response objectForKey:@"result"]) objectForKey:@"geometry"];
    NSDictionary * coords = [dicGeo objectForKey:@"location"];
    NSNumber * lat = [coords objectForKey:@"lat"];
    NSNumber * lng = [coords objectForKey:@"lng"];
    NSString * coordinates = [NSString stringWithFormat:@"%@,%@", lat.description, lng.description];
    handler(coordinates, error);
 failure:^(AFHTTPRequestOperation *operation, NSError *error) 
    NSLog(@"%@", error);
];

[requestOperation start];

然后调用这个服务:

   [Connection requestLocation:@"google reference (it is not)" completionBlock:^(NSString *coordinates, NSError *error)  
    //Your code with results.
 

【讨论】:

我认为 AFJSONRequestOperation 现在不在 AFNetworking 之下:***.com/questions/21294178/… 您应该使用响应序列号:operation.responseSerializer = [AFJSONResponseSerializer serializer]; 但无论如何:您的代码必须使用 AFNetworking 1.x,因为 AFHTTPClient 在 AFNetworking 2 中不可用。 链接中的答案也建议这样做。【参考方案2】:

我只是接触了 AFNetworking 的皮毛。从我所见,大部分似乎都使用了第三种方法,块。

块有点新,与委托和通知不同。

块是 C 函数指针的扩展,可让您在调用方法时将代码传递给方法。

使用块的常见设计模式是创建一个采用完成块的方法。完成块是在异步请求完成时调用的一段代码。

以AFNewtworking方法HTTPRequestOperationWithRequest为例。该方法接受一个成功块,如果请求成功则调用它,以及一个失败块,如果请求失败则调用它。

【讨论】:

【参考方案3】:

阻止是使用 IMO 的最简单方法。您不需要实现额外的委托方法,也不需要任何构造。

基本上像这样定义你的包装器。

typedef void(^SampleRequestCompletion)(NSError *error, id data);

- (void)GET:(NSString *)URLString
 parameters:(NSDictionary *)parameters
 completion:(SampleRequestCompletion)completion

[self GET:URLString parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) 

    // Do what you want

    if (completion) 
        completion(nil, data);
    

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

// Failure case

    if (completion) 
        completion(error,nil);
    
];

并从这样的任何对象调用此方法,

    [self GET:path parameters:dictionary completion:^(NSError *error, id data) 

];

因此,您可以管理呼叫以成功或失败结束时要执行的操作。

【讨论】:

【参考方案4】:

按照教程的建议,我们可以将 Web 服务相关的代码提取到一个更像模型级别的模块中。考虑到网络模块和视图之间的通信,视图在单例 Web 服务客户端上调用/启动请求,一旦响应返回,通常的工作流程就会将结果发送到视图控制器并在视图中显示数据。我们不需要将任何东西返回给网络模块。

所以这个工作流程更像是一个通知而不是委托。并将 V 设置为 M 的代表,这很奇怪。

通知:嘿,伙计,我已经完成了我的工作,轮到你了。 代表团:嘿,伙计,我做了很多,现在我需要你掩护/备份/提供一些任务,然后我会继续/完成工作。

在某些情况下,很难选择哪个更好。对于 AFNetworking,我认为 Notification 方法更好。

【讨论】:

通知不能说“嘿你”,因为不知道谁在听。它是一种广播机制。委托也涵盖了网络,正如网络委托人可能会说的:“我已经完成了获取。委托,做任何你想做的事”。苹果自己的网络课程就是这样工作的。另外 networqking 不属于模型,而是控制器。但不是视图控制器。 是的,您可能对网络模块是否属于控制器是正确的。但正如教程所示,我们可以继承 AFNetworking 类并将 Web 服务抽象为一个模块。由于大多数 Web 服务更像是一个数据源,恕我直言,至少我们可以将服务模块视为模型,我们只需通过定义的接口获取/放置(读/写)数据,而不管模块是网络的还是持久的。在 Unix 世界中,我们可以使用相同的进程从网络或文件中读取数据。 对于通知或委托,我仍在调查,但目前我选择了通知。我们不能仅仅因为 Apple 使用委托而缩小范围。在我的实践中,通知将获得更简洁的代码,并且如果您要更改设计,则可以将调用者和被调用者解耦,尤其是在参数上。使用字典比使用显式参数更方便。我刚刚从我的经验中得到这些,我发现有时我需要更改委托方法签名。如果你有更好的实践,你会分享更多细节吗?提前谢谢你。 正如所有其他海报所说:块。 谢谢!在做了一些实验后,我发现块可能是另一种选择。听起来我需要更好地设计代码结构来解耦错误处理。

以上是关于AFNetworking 回调 - 委托还是通知?的主要内容,如果未能解决你的问题,请参考以下文章

swift 2:与objective-c相比,推送通知没有委托回调

第十七章 委托

c#子线程执行完怎么通知主线程(转)

通话期间未调用远程通知回调

UIAlertView 隐藏/删除时调用的通知或委托

NSURLConnection(作为 AFNetworking 的一部分)不调用 NSURLConnectionDataDelegate 委托