如何从NSURLSessionDataTask完成处理程序返回NSData

时间:2014-07-20 15:05:05

标签: ios objective-c ios7 nsurlsession

我正在尝试创建一个可以用来调用帖子Web服务的简单类。

除非我无法返回NSData

,否则一切正常

这是我的代码:

+ (NSData *)postCall:(NSDictionary *)parameters fromURL:(NSString *)url{
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    NSMutableArray *pairs = [[NSMutableArray alloc]init];
    for(NSString *key in parameters){
        [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, parameters[key]]];
    }
    NSString *requestParameters = [pairs componentsJoinedByString:@"$"];
    NSURL *nsurl = [NSURL URLWithString:url];
    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:nsurl];
    [urlRequest setHTTPMethod:@"POST"];
    [urlRequest setHTTPBody:[requestParameters dataUsingEncoding:NSUTF8StringEncoding]];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        //return data;
    }];
    [dataTask resume];

    return nil;
}

请注意我有//return data,但它给了我这个错误

 Incompatible block pointer types sending 'NSData *(^)(NSData *__strong, NSURLResponse *__strong, NSError *__strong)' to parameter of type 'void (^)(NSData *__strong, NSURLResponse *__strong, NSError *__strong)'

我的问题是:

  1. 我的方式好还是将来会给我带来麻烦?我没有要下载的图像,而且我没有要上传的内容,我只需要发送简单的字符串数据并接收简单的字符串数据。或者,每个函数中的代码会更好吗?

  2. 如何退回数据?

2 个答案:

答案 0 :(得分:8)

您不能只返回数据(因为NSURLSessionDataTask以异步方式运行)。您可能希望使用自己的完成块模式,类似于completionHandler方法的dataTaskWithRequest

因此,您可以将自己的块参数添加到您的方法中,您将在dataTaskWithRequest方法completionHandler内调用

+ (NSURLSessionDataTask *)postCall:(NSDictionary *)parameters fromURL:(NSString *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler {

    // create your request here ...

    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (completionHandler)
            completionHandler(data, response, error);
    }];

    [dataTask resume];

    return dataTask;
}

或者,因为这个dataTaskWithRequest在后​​台线程上运行,所以确保将完成处理程序分派回主队列有时很有用,例如

NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (completionHandler)
        dispatch_async(dispatch_get_main_queue(), ^{
            completionHandler(data, response, error);
        });
}];

请注意,除此之外,我认为返回NSURLSessionDataTask引用是很好的,如上所述,因此(a)调用者可以确保成功创建数据任务; (b)你有NSURLSessionTask引用可用于取消任务以防万一,在某个未来日期,你希望能够因某种原因取消请求(例如,用户从中取消视图控制器请求已发出。)

无论如何,你然后用:

调用它
NSURLSessionTask *task = [MyClass postCall:parameters fromURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // put whatever code you want to perform when the asynchronous data task completes
}];

if (!task) {
    // handle failure to create task any way you want
}

你问:

  

我的方式好还是将来会给我带来麻烦?我没有下载的图像,也没有任何要上传的内容,我只需发送[一些]简单的字符串数据并接收[简单]字符串数据。或者,每个函数中的代码会更好吗?

如果您正在接收简单的字符串数据,我建议您以JSON格式撰写回复,然后在postCall中使用完成块使用NSJSONSerialization来提取回复。使用JSON可以让应用程序更轻松地区分成功响应和可能还会返回字符串响应的各种服务器相关问题。

因此,我们假设您修改了服务器代码以返回如下响应:

{"response":"some text"}

然后您可以修改postCall来解析该响应,如下所示:

+ (NSURLSessionDataTask *)postCall:(NSDictionary *)parameters fromURL:(NSString *)url completionHandler:(void (^)(NSString *responseString, NSError *error))completionHandler {

    // create your request here ...

    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (completionHandler) {
            if (error) {
                completionHandler(nil, error);
            } else {
                NSError *parseError = nil;
                NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];

                completionHandler(responseDictionary[@"response"], parseError);
            }
        }
    }];

    [dataTask resume];

    return dataTask;
}

就你的基本问题而言,postCall这样的方法是否有意义,是的,我认为将创建请求的细节放在一个方法中是完全合理的。我在您的实现中的一个小保留是您决定使其成为类方法而不是实例方法。您目前正在为每个请求创建新的NSURLSession。我建议将postCall作为一个实例方法(如果你想要的话是单例),然后将会话保存为类属性,你可以设置一次,然后在后续查询中重复使用。

答案 1 :(得分:1)

您应该使用块方法。

首先定义一个块

typedef void (^OnComplete) (NSData *data);

使用以下方法

+ (void)postCall:(NSDictionary *)parameters fromURL:(NSString *)url withBlock:(OnComplete)block; {

     NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
        NSMutableArray *pairs = [[NSMutableArray alloc]init];
        for(NSString *key in parameters){
            [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, parameters[key]]];
        }
        NSString *requestParameters = [pairs componentsJoinedByString:@"&"];
        NSURL *myURL = [NSURL URLWithString:url];
        NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:myURL];
        [urlRequest setHTTPMethod:@"POST"];
        [urlRequest setHTTPBody:[requestParameters dataUsingEncoding:NSUTF8StringEncoding]];
        NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            block(data);
        }];
        [dataTask resume];
   }