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

Posted

技术标签:

【中文标题】如何从 NSURLSessionDataTask 完成处理程序返回 NSData【英文标题】:How to return NSData from NSURLSessionDataTask completion handler 【发布时间】:2014-07-20 15:05:04 【问题描述】:

我正在尝试创建一个简单的类,我可以使用它来调用发布网络服务。

除了我无法返回 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)'

我的问题是:

    我的方法好不好,否则将来会给我带来麻烦?我没有要下载的图像,也没有要上传的内容,我只需要发送简单的字符串数据并接收简单的字符串数据。或者最好把每个函数中的代码独立出来?

    请问如何返回数据?

【问题讨论】:

顺便说一句,您将请求参数与$ 连接起来。那是正确的吗?一般你使用&。您通常还使用CFURLCreateStringByAddingPercentEscapes(不是stringByAddingPercentEscapesUsingEncoding)对parameters[key] 值进行百分比转义。见***.com/a/23635327/1271826。 【参考方案1】:

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

因此,您可以将自己的块参数添加到您的方法中,您将从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


你问:

我的方式好不好,否则将来会给我带来麻烦?我没有要下载的 [an] 图像,也没有要上传的任何内容,我只需要发送 [some] 简单的字符串数据并接收 [simple] 字符串数据。还是最好把每个函数中的代码独立出来?

如果您收到简单的字符串数据,我建议您以 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。 @Rob 和我也修复了原版中的错误。 谢谢罗伯。昨天我在帖子中用糟糕的语法、非标准代码格式修复了很多错误,我一定只是看到了错误并修复了它们。在其他帖子中,我想回来并修复原始海报的拼写错误,并且一定错过了这个帖子中的那些。【参考方案2】:

你应该使用块方法。

首先定义一个块

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];
   

【讨论】:

我非常感谢这些努力。抱歉,我今晚不能检查,因为我很忙。不过,我明天会检查一下。无论如何 +1。

以上是关于如何从 NSURLSessionDataTask 完成处理程序返回 NSData的主要内容,如果未能解决你的问题,请参考以下文章

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

如何获取数据以从 NSURLSessionDataTask 返回

UIAlertController从NSURLSessionDataTask出现时使应用程序崩溃?

appdelegate 中的 NSURLSessionDataTask

什么时候使用 NSURLSessionDownloadTask 和 NSURLSessionDataTask?

如何将“C++ 完成处理程序”传递给 NSURLSessionDataTask 以处理它返回的数据?