网络请求(NSURL、NSURLRequest、NSURLSessionDataTask、协议回调与 JSON 数据的基本流向)

发布时间:2026/7/19 3:12:03
网络请求(NSURL、NSURLRequest、NSURLSessionDataTask、协议回调与 JSON 数据的基本流向) 网络请求(NSURL、NSURLRequest、NSURLSessionDataTask、协议回调与 JSON 数据的基本流向)文章目录网络请求(NSURL、NSURLRequest、NSURLSessionDataTask、协议回调与 JSON 数据的基本流向)一、整体流程二、NSURL三、NSURLRequest / NSMutableURLRequest四、NSURLSession五、NSURLSessionDataTask六、Block回调和代理回调七、JSON数据解析(NSJSONSerialization)一、整体流程在OC中做一次网络请求本质是这几个对象配合的过程NSURL地址 ↓ NSURLRequest把地址包装成一份请求单方法、Header、Body... ↓ NSURLSession会话管理器负责发起、调度、管理所有任务 ↓ NSURLSessionDataTask具体的一次网络任务session 用它来执行 request ↓ 数据回来了 —— 通过 completionHandlerBlock或者 Delegate协议回调拿到 NSData ↓ NSJSONSerialization 把 NSData 解析成 NSDictionary / NSArray即 JSON 对象总结NSURL 是地址NSURLRequest 是包装好地址参数的信封NSURLSession 是邮局NSURLSessionDataTask 是这一次寄送的具体包裹回调Block 或 Delegate是收件通知NSData 是包裹里的东西JSON 解析是拆包裹二、NSURLNSURL只负责表示一个地址可以是网络地址也可以是本地文件路径// 最简单的创建方式NSURL*url[NSURL URLWithString:https://api.example.com/users];// 如果 URL 中带有中文或特殊字符需要先做 URL 编码否则可能创建失败返回 nilNSString*rawStringhttps://api.example.com/search?qym;NSString*encodedString[rawString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];NSURL*url2[NSURL URLWithString:encodedString];例如一个天气接口的NSURLhttps://api.weather.com/v1/current?cityBeijingkeyabcdef123456langzh它的组成就是协议(https://)服务器地址(api.weather.com)访问接口(/v1/current),这里表示访问实时天气查询参数(Query)(?cityBeijingkeyabcdef123456langzh)查询参数是按照自己想查的东西对应需要的注意点一般在GET方法中我们在URL中直接写入查询参数在POST方法中我们一般将其写在HTTPBody中URL 拼接查询参数时推荐用NSURLComponents比手动拼字符串更安全NSURLComponents*components[NSURLComponents componentsWithString:https://api.example.com/search];components.queryItems[[NSURLQueryItem queryItemWithName:qvalue:你好世界],[NSURLQueryItem queryItemWithName:pagevalue:1]];NSURL*url3components.URL;// NSURLComponents 会自动帮你做好编码三、NSURLRequest / NSMutableURLRequestNSURL只是地址NSURLRequest才是真正描述这次请求要怎么发的对象请求方法GET/POST、超时时间、请求头、请求体等。NSURLRequest不可变创建后不能改。适合简单的 GET。NSMutableURLRequest可变可以设置 Header、Body、HTTPMethod是最常用的类型。NSURL*url[NSURL URLWithString:https://api.example.com/users/1];NSMutableURLRequest*request[NSMutableURLRequest requestWithURL:url];request.HTTPMethodGET;// 请求方法默认就是 GETrequest.timeoutInterval15.0;// 超时时间单位秒[request setValue:application/jsonforHTTPHeaderField:Content-Type];//请求头[request setValue:Bearer xxx-token-xxxforHTTPHeaderField:Authorization];//请求头属性说明属性说明HTTPMethod字符串GET/POST/PUT/DELETE等allHTTPHeaderFields/setValue:forHTTPHeaderField:请求头HTTPBodyNSData类型POST/PUT 时放请求体比如 JSON 序列化后的数据timeoutInterval超时时间cachePolicy缓存策略比如NSURLRequestReloadIgnoringLocalCacheData表示不用本地缓存四、NSURLSessionNSURLSession是真正发起网络请求NSURLRequest只是配置要求。它自己不直接产生结果而是通过它来创建NSURLSessionTaskNSURLSessionDataTask/UploadTask/DownloadTask常见的获取方式// 1. 单例会话最常用配置简单适合大多数普通请求NSURLSession*session[NSURLSession sharedSession];// 2. 自定义配置的会话可以设置超时、是否走蜂窝网络、缓存策略等NSURLSessionConfiguration*config[NSURLSessionConfiguration defaultSessionConfiguration];config.timeoutIntervalForRequest20.0;config.allowsCellularAccessYES;NSURLSession*customSession[NSURLSession sessionWithConfiguration:config];// 3. 带 delegate 的会话想用协议回调方式接收数据时必须这样创建NSURLSession*delegateSession[NSURLSession sessionWithConfiguration:config delegate:selfdelegateQueue:nil];// nil 表示系统自动创建一个串行队列注意点:我们注意到超时时间缓存策略不是在NSURLRequest设置过一次吗在NSURLSession又设置一遍有什么区别需要说的是NSURLRequest是单次请求级别。NSURLSessionConfiguration是整个 Session 级别Request优先级是高于Session的也就是说Request会覆盖Session的设置三种 SessionConfigurationdefaultSessionConfiguration使用磁盘缓存行为和普通网络请求一致。ephemeralSessionConfiguration不写磁盘全部数据存内存App 退出即清空类似浏览器无痕模式。backgroundSessionConfiguration:支持 App 进入后台后继续传输常用于大文件上传下载。五、NSURLSessionDataTasksession拿到request后通过它创建一个NSURLSessionDataTask这个 task 就代表这一次具体的网络请求。NSURLSessionDataTask*task[session dataTaskWithRequest:request completionHandler:^(NSData*_Nullable data,NSURLResponse*_Nullable response,NSError*_Nullable error){// 请求完成后的回调会在子线程执行if(error){NSLog(请求失败: %,error.localizedDescription);return;}// data 就是服务器返回的原始二进制数据通常是 JSON 的字节流}];//必须手动 resume 才会真正发出请求[task resume];关键点dataTaskWithRequest:有两种重载dataTaskWithRequest:completionHandler:—— Block 方式一次性拿到完整结果简单直接。dataTaskWithRequest:不传 completionHandler —— 必须配合delegate数据是分段、多次回调给你的适合需要监控下载进度、大文件流式处理的场景。两者不能同时用用了 completionHandler 的 taskdelegate 里的数据回调方法如didReceiveData:就不会被触发。六、Block回调和代理回调BlockcompletionHandlerDelegate协议回调使用难度简单代码集中稍复杂逻辑分散在多个代理方法里数据接收方式一次性拿到完整NSData分多次通过didReceiveData:累积拼接适用场景普通的增删改查请求需要进度回调、大文件下载、需要精细控制比如证书校验、重定向处理回调所在线程子线程非主线程由delegateQueue决定默认也是子线程共同点无论哪种方式回调都不在主线程如果要更新 UI必须手动切回主线程dispatch_async(dispatch_get_main_queue(),^{self.label.text更新UI;});代理回调interfaceViewController()NSURLSessionDataDelegateproperty(nonatomic,strong)NSMutableData*receivedData;// 用来拼接分段到达的数据endimplementationViewController// 1. 收到服务器响应头时调用此时还没有 body 数据-(void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask didReceiveResponse:(NSURLResponse*)response completionHandler:(void(^)(NSURLSessionResponseDisposition disposition))completionHandler{NSHTTPURLResponse*httpResponse(NSHTTPURLResponse*)response;NSLog(状态码: %ld,(long)httpResponse.statusCode);self.receivedData[NSMutableData data];// 容器接收数据// completionHandler告诉系统继续接收数据completionHandler(NSURLSessionResponseAllow);}// 2. 数据分段到达时反复调用一个大的响应体可能会触发好多次-(void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask didReceiveData:(NSData*)data{[self.receivedData appendData:data];// 把这一段拼接起来}// 3. 整个 task 结束时调用-(void)URLSession:(NSURLSession*)session task:(NSURLSessionTask*)task didCompleteWithError:(nullable NSError*)error{if(error){NSLog(请求失败: %,error.localizedDescription);return;}// 数据已经完整拼接好,开始 JSON 解析NSError*jsonError;id jsonObject[NSJSONSerialization JSONObjectWithData:self.receivedData options:0error:jsonError];if(jsonError){NSLog(JSON 解析失败: %,jsonError);return;}NSLog(解析结果: %,jsonObject);}end七、JSON数据解析(NSJSONSerialization)服务器返回的NSData本质是一串 JSON 格式的字节流需要用NSJSONSerialization把它转换成 OC 对象NSDictionary/NSArray才能使用NSError*error;id jsonObject[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:error];if(error){NSLog(JSON 解析失败: %,error.localizedDescription);return;}// JSON 顶层可能是字典也可能是数组务必先判断类型再使用避免 crashif([jsonObject isKindOfClass:[NSDictionary class]]){NSDictionary*dict(NSDictionary*)jsonObject;NSString*namedict[name];NSLog(name %,name);}elseif([jsonObject isKindOfClass:[NSArray class]]){NSArray*array(NSArray*)jsonObject;NSLog(数组长度: %lu,(unsignedlong)array.count);}反过来把 OC 对象序列化成 JSON Data用于 POST 请求体NSDictionary*params{username:darian,password:123456};if([NSJSONSerialization isValidJSONObject:params]){// 先校验是否可序列化NSError*error;NSData*bodyData[NSJSONSerialization dataWithJSONObject:params options:0error:error];if(!error){request.HTTPBodybodyData;}}常用 options值作用NSJSONReadingMutableContainers解析出的字典/数组是可变的NSMutableDictionary/NSMutableArrayNSJSONReadingMutableLeaves解析出的字符串等叶子节点也是可变的NSJSONReadingFragmentsAllowed允许顶层不是字典/数组比如单独一个数字或字符串NSJSONWritingPrettyPrinted写入时用生成带缩进换行的、易读的 JSON