有时在请求网络数据时并没有返回NSData,而是返回OS_dispatch_data
NSData实际上是仅仅提供接口的类簇,并且有很多种特别的实现,推断OS_dispatch_data是这样一种实现
NSData is actually a class cluster which just provides the interface, and there are multiple special implementations for it around. It appears that OS_dispatch_data is such a special implementations made to pass data objects around blocks
OS_dispatch_data 是 dispatch_data_t (在iOS7和Mavericks上)可以用NSData无缝桥接,你可以很轻松的通过向下转型获得NSData
OS_dispatch_data is dispatch_data_t which has toll-free bridging with NSData on iOS 7 and Mavericks. You can simply cast it to NSData *.
So, in your case you can write:
NSData *dataCast = self.message.data;
NSString *dataString = [[NSString alloc] initWithData:dataCast encoding:NSUTF8StringEncoding];
一件小事引发的血案:当我们使用原生API发送一个Get请求时,首先想到下面的方法
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"XXXX"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[task resume];
如果使用AFN则使用下面的方法
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes =
[manager.requestSerializer setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
[manager GET:@"XXXX" parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
但是上面返回的确是OS_dispatch_data,而下面的responseObject返回的确是NSData,虽然OS_dispatch_data可以无缝桥接转换,但我们都知道AFN3.0封装的就是NSURLSession,那么从这一点入手,分析问题。
发现不一样的是User-Agent 和 Accept-Language 虽然这个并不会影响返回数据,但是还是看看AFN的源码吧
userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
// Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
[[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
float q = 1.0f - (idx * 0.1f);
[acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
*stop = q <= 0.5f;
}];
[self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];
与此同时 ,我无聊的将请求改为这样:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{
@"User-Agent":@"XXX/3.5.0 (iPhone; iOS 10.0; Scale/2.00)",
@"Accept-Language":@"zh-Hans-US;q=1, en;q=0.9"
}; NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"XXXX"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[task resume];
显然,这与请求头毫无关系,那么就看看AFN是如何封装的吧
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
上面是AFN最内层方法,里面有我们想得到的dataTask = [self.session dataTaskWithRequest:request]; 瞬间恍然大悟,于是无聊的将网络请求更改为这样:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.HTTPAdditionalHeaders = @{
@"User-Agent":@"XXXX/3.5.0 (iPhone; iOS 10.0; Scale/2.00)",
@"Accept-Language":@"zh-Hans-US;q=1, en;q=0.9"
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"XXXX"]];
[task resume];
遵守NSURLSessionDataDelegate,定义一个NSMutableData变量存储下载的NSData,实现下面的代理方法
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
self.allData = [NSMutableData data];
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
[self.allData appendData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"result: %@", self.allData);
}