iOS开发 ☞ OS_dispatch_data

当使用原生API或AFNetworking进行网络请求时,可能会收到OS_dispatch_data类型的响应,而非常见的NSData。本文探讨了OS_dispatch_data与NSData之间的转换,并通过分析请求头和AFNetworking源码,揭示了为何会得到OS_dispatch_data。最后,提供了实现NSURLSessionDataDelegate来手动处理数据的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有时在请求网络数据时并没有返回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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值