如何从NSURLSessionDataTask完成处理程序返回NSData [英] How to return NSData from NSURLSessionDataTask completion handler

查看:136
本文介绍了如何从NSURLSessionDataTask完成处理程序返回NSData的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个简单的类,我可以用它来调用发布Web服务.

I am trying to make a simple class that I can use to call a post web service.

一切正常,除了我无法退回NSData.

Everything is working perfectly except that I am not able to return the 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,但它给了我这个错误

Please notice that I have //return data but it gives me this error

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

我的问题是:

  1. 我的方式是否还好,否则将来会给我带来麻烦?我没有要下载的图像,也没有任何要上传的图像,我只需要发送简单的字符串数据并接收simpe字符串数据.还是最好让这些代码独立存在于每个函数中?

  1. Is my way good or it will cause me problems in the future? I don't have image to download and I don't have anything to upload, I just have to send simple string data and receive simpe string data. Or it will be better to but that code in each function independently?

请问如何返回数据?

推荐答案

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

You cannot just return the data (because the NSURLSessionDataTask runs asynchronously). You probably want to employ your own completion block pattern, similar to the completionHandler of the dataTaskWithRequest method.

因此,您将自己的block参数添加到方法中,您将从dataTaskWithRequest方法的completionHandler内部调用该参数:

So, you would add your own block parameter to your method, that you'll invoke from inside the dataTaskWithRequest method's 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在后台线程上运行,所以确保将完成处理程序分派回主队列有时很有用,例如

Or, because this dataTaskWithRequest runs on a background thread, it’s sometimes useful to make sure to dispatch the completion handler back to the main queue, e.g.

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引用,可用于取消任务,以防万一在某个将来的日期,您希望能够出于某种原因取消该请求(例如,用户关闭了视图控制器,请求已发出).

Note, as an aside, I think it's good to return the NSURLSessionDataTask reference, like above, so (a) the caller can make sure the data task was successfully created; and (b) you have the NSURLSessionTask reference that you can use to cancel the task in case, at some future date, you want to be able to cancel the request for some reason (e.g. the user dismisses the view controller from which the request was issued).

无论如何,您然后可以通过以下方式调用它:

Anyway, you'd then invoke this with:

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
}


您问:


You ask:

我的方法是否不错,否则将来会给我带来麻烦?我没有要下载的图片,也没有要上传的图片,我只需要发送[一些]简单的字符串数据并接收[简单]的字符串数据.还是最好让每个函数中的代码独立存在?

Is my way good or it will cause me problems in the future? I don't have [an] image to download and I don't have anything to upload, I just have to send [some] simple string data and receive [simple] string data. Or it will be better to but that code in each function independently?

如果您要接收回简单的字符串数据,我建议您以JSON格式编写响应,然后将postCall中的完成块使用NSJSONSerialization提取响应.使用JSON,使应用程序更容易区分成功响应和各种服务器相关问题,这些问题也可能返回字符串响应.

If you're receiving simple string data back, I'd suggest composing your response in JSON format, and then having the completion block in postCall use NSJSONSerialization to extract the response. Using JSON makes it easier for the app to differentiate between successful response and a variety of server related problems that might also return string responses.

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

So, let's say you modified your server code to return a response like so:

{"response":"some text"}

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

Then you could modify postCall to parse that response like so:

+ (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设置为实例方法(如果需要,可以使用单例),然后将会话另存为类属性,您只需设置一次即可,然后在后续查询中重新使用.

In terms of your underlying question, whether a method like postCall makes sense, yes, I think it makes perfect sense to put the details of creating the request in a single method. My minor reservation in your implementation was your decision to make it a class method rather than an instance method. You're currently creating a new NSURLSession for each request. I'd suggest making postCall an instance method (of a singleton if you want) and then saving the session as a class property, which you set once and then re-use on subsequent queries.

这篇关于如何从NSURLSessionDataTask完成处理程序返回NSData的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆