【问题标题】:Are there cleaner methods to handling async http requests than delegation in Objective-C?是否有比 Objective-C 中的委托更简洁的方法来处理异步 http 请求?
【发布时间】:2014-10-29 10:56:29
【问题描述】:

我正在构建一个 iPhone 应用程序,该应用程序大量利用了我用 php 编写的 Web 服务。现在我有一个文件(API.h/API.m),其中包含通过 NSURLConnection 在我的 Web 服务上调用 php 函数的方法。

方法如:

-(void) mediaAdd:(UIImage *)image withDelegate: (id<apiRequest>) delegate;
-(void) mediaGet:(NSString *)imageURL withDelegate: (id<apiRequest>) delegate;

这些方法由应用程序中的控制器(由 MVC 模式定义的控制器)调用,这些方法将控制器作为委托。

一旦来自 API.h/API.m 中被调用方法的请求完成,在请求完成时调用 API.h/API.m 中的 NSURLConnection 委托方法,然后调用委托方法...

-(void) serverResponse:(NSDictionary *) response fromCall:(NSString *)call;

...这当然是在调用 API.h/API.m 中方法的控制器上执行的。

当控制器需要进行多个 API 调用时,就会出现我的问题,或者说混乱。因为我只有一个委托方法,所以我通过serverResponse方法中的call参数来区分不同的调用。

下面是 serverResponse 方法实例的框架:

//API Delegate
-(void)serverResponse:(NSDictionary *)response fromCall:(NSString *)call {
    bool local = false;
    NSString *callbackString;
    if (local) {
        callbackString = @"http://localhost/";
    } else {
        callbackString = @"http://secret.com/";
    }
    if ([call isEqualToString:[NSString stringWithFormat:@"%@user/add",callbackString]]) {
        //user/add was called
        if (![[response objectForKey:@"status"] isEqualToString:@"fail"]) {
            if ([[response objectForKey:@"status"] isEqualToString:@"fail"]) {
                if ([[response objectForKey:@"reason"] isEqualToString:@"udid already taken"]) {
                     //UDID taken
                }
            } else {

                //UDID not taken, we're good to go



            }

        } else {
            NSLog(@"fail");
        }
    } else if([call isEqualToString:[NSString stringWithFormat:@"%@moment/get",callbackString]]){
        //moment/get was the api call
    } else if([call isEqualToString:[NSString stringWithFormat:@"%@printPictures/printPic",callbackString]]){

        //printPictures/printPic was the api call
    } else {
        NSLog(@"wrong call");
    }



}

我的问题是,我应该继续为每个 API 调用创建一个委托方法吗?还是我想太多了,实际上有一个非常简单的方法来处理这个问题。也许是设计模式或结构?

谢谢! - 迈克

【问题讨论】:

  • 题外话观察,不过现在看骨架,我有很多冗余代码..哈哈。只是注意到我注意到了这一点,所以我知道这是个问题。
  • 您可以传递完成块而不是委托。
  • 您当然意识到可以为给定的委托协议创建多个委托对象?要解决您的问题,只需为每个 API 调用创建一个对象。
  • Hot Licks,我在我的问题中提到了该解决方案,但我不想通过它,除非它是最后一个可用的选项。
  • 请注意,“单独的委托对象”与“单独的委托方法”不同。您可以拥有同一个委托类的多个副本,实现相同的委托方法。

标签: ios objective-c api nsurl delegation


【解决方案1】:

我通过一个带有回调块的单独下载类来做这种事情。控制器创建一个类的实例,并在回调中接收结果,然后释放下载类。您需要进行的每个调用都会创建自己的 Downloader 类实例,因此您无需跟踪哪个响应与哪个调用对应。

    Downloader *dl = [Downloader new];
    NSURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"...."]] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];

    [dl downloadWithRequest:(NSURLRequest *) request completion:^(NSData *data, NSError *error) {
        if (!error ) {
            // do whatever you need to do with the raw data here
            NSLog(@"got the data");
        }else{
            NSLog(@"%@",error);
        }
    }];

这是我实现 Downloader 类的方法。

typedef void(^compBlock)(NSData *data, NSError *error);

@interface Downloader : NSObject <NSURLConnectionDataDelegate>

-(void)downloadWithRequest:(NSURLRequest *) request completion:(compBlock) completion;

@end

实现文件,

@interface Downloader()
@property (copy,nonatomic) compBlock completionHandler;
@property (strong,nonatomic) NSMutableData *receivedData;
@end

@implementation Downloader

-(void)downloadWithRequest:(NSURLRequest *) request completion:(compBlock)completion {
    NSURLConnection *con = [NSURLConnection connectionWithRequest:request delegate:self];
    if (con) {
        self.receivedData = [NSMutableData new];
        self.completionHandler = completion;
    }
}


-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    self.receivedData.length = 0;
}



-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [self.receivedData appendData:data];
}



-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    self.completionHandler(nil, error);
}



-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
    self.completionHandler(self.receivedData, nil);
}

【讨论】:

  • 这种方法不需要同步请求吗?或者可能是不同队列上的同步请求?
  • @MichaelKing,不,在 Downloader 类中,我将 NSURLConnection 与通常的委托方法一起使用。在 didFailWithError 或 connectionDidFinishLoading 中调用回调块。
  • 我没有意识到回调块可以在一个单独的位置。我想我需要阅读更多关于块的内容,以及它们是如何工作的。你手头有什么好的资源吗?
  • @MichaelKing,不,除了 Apple 的文档之外,我不知道任何特别好的资源。如果你想看看我是如何实现的,我可以发布下载器类的代码。
  • 那太好了!谢谢!
【解决方案2】:

当控制器需要进行多个 API 调用时,就会出现我的问题,或者说混乱。

对于这种东西,你真的应该看看NSURLSession和相关的类。 NSURLSession 提供像 -sendAsynchronousRequest:queue:completionHandler: 这样的方法,让您无需提供自己的委托对象即可完成任务——会话将为您提供默认委托,并且默认委托将在任务完成时调用您的完成处理程序。

当然,您仍然可以选择提供自己的委托,并且每个任务都有一个标识符,这样您的委托就可以比使用NSURLConnection 更容易地跟踪哪个任务。

我不会在这里详细介绍 NSURLSession 的所有好处,因为您可以自己 read about them,但它们很重要。如果您正在编写新代码,您几乎可以肯定应该使用NSURLSession 而不是NSURLConnection

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-25
    • 2016-08-21
    • 1970-01-01
    相关资源
    最近更新 更多