iOS:网络请求再学习

文章目录

网络请求再学习

网络请求的步骤

  1. 创建请求地址

确定接口地址,处理路径和查询参数(如 ?key=xxx&city=beijing)。

  1. 创建请求类

  2. 创建会话

    1. 配置会话

    创建 NSURLSessionConfiguration,设置超时时间、缓存策略、是否允许蜂窝网络等。这一步决定了网络请求的"基调"(默认、临时、后台)。

    1. 创建会话

    利用上一步的配置,生成 NSURLSession 实例。如果不需要代理监听(如进度),直接用 [NSURLSession sharedSession] 即可。

  3. 生成任务

根据业务需求选择任务类型(DataTask / DownloadTask / UploadTask),并传入 URL 或 Request 对象。此时任务处于 suspended(挂起)状态,还未开始联网。

  1. 启动任务

调用 [task resume]

  1. 处理响应数据

如果自定义了 delegate 且会话不再使用,需调用 [session finishTasksAndInvalidate] 或 [session invalidateAndCancel] 来打破 session 对 delegate 的强引用。

  1. 清理会话

创建请求地址

在开始学习的时候, 我们创建URL一般是直接使用字符串, 这种方式只适合静态地址 (没有中文, 没有空格, 没有特殊符号)

NSURL *url = [NSURL URLWithString:@"https://api.weatherapi.com/v1/forecast.json"];

创建请求类

在 NSURLSession 中,苹果提供了一个非常便捷的方法:

objc 复制代码
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

如果你直接传入 URL,系统会在内部自动帮你创建一个默认的 NSURLRequest (默认使用 GET 方法,默认超时时间,默认缓存策略)。在这种情况下,不需要写 NSURLRequest 的代码,任务就直接生成了

通过这种方式直接生成任务就不用去专门创建请求类了

完整步骤 (显式创建)

如果请求稍微复杂一点(比如 POST 请求、需要加 Headers (如 Token)、需要上传 Body 数据),你就必须 显式地创建 NSMutableURLRequest。

objc 复制代码
// 创建请求类
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL: url];
    // HTTP 方法
    request.HTTPMethod = @"GET";
    // 时间阈值
    request.timeoutInterval = 15;

创建会话

在iOS中, 创建会话共有3种方式,分别对应不同的业务复杂度

1. 共享单例会话

直接使用系统提供的全局单例对象。

复制代码
NSURLSession *session = [NSURLSession sharedSession];
  • 适用场景:简单的、一次性的 GET/POST 请求,且不需要监听上传/下载进度。
  • 特点 :使用系统默认配置(类似浏览器),无法自定义超时时间、缓存策略 ,且无法设置 Delegate 。所有回调只能通过 completionHandler(闭包)处理。
  • 内存管理:无需管理,它是个单例,App 生命周期内一直存在。

2. 基于配置的会话(无代理,仅闭包回调)

使用 NSURLSessionConfiguration 来创建,但不设置代理 (传入 nil),所有的数据返回都靠 Block 闭包。

复制代码
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.timeoutIntervalForRequest = 30.0;

// 注意:delegate 传 nil,delegateQueue 传 nil 或主队列
NSURLSession *session = [NSURLSession sessionWithConfiguration:config 
                                                      delegate:nil 
                                                 delegateQueue:[NSOperationQueue mainQueue]];
  • 适用场景:需要自定义超时、缓存策略或额外 Headers,但不需要监听进度(如普通的 JSON 接口请求)。
  • 特点 :灵活度高于 sharedSession,但由于没有 Delegate,无法处理需要身份验证(如客户端证书)或重定向的复杂场景。
  • 内存管理 :因为 delegate 是 nil,不存在循环引用问题,会话用完后系统会自动释放,通常不需要手动 invalidate。

NSURLSession* session = [NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration]]; 相当于

objc 复制代码
 NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config 
                                                   delegate:nil 
                                              delegateQueue:[NSOperationQueue mainQueue]];

方法的第三个参数 delegateQueue 指的是回调队列

  • 含义 :指定所有代理方法(以及 completionHandler 闭包)在哪个线程执行。
  • 作用 :传入主队列(Main Queue),意味着回调闭包会在主线程执行。

虽然 delegate 传的是 nil,但这个 delegateQueue 依然有效。当使用带 completionHandler 的 dataTaskWithRequest: 时,这个闭包会在指定的 delegateQueue 上被调用。因为传了 mainQueue,所以成功/失败闭包直接运行在主线程,所以可以安全地在闭包内部更新 UI(如 self.label.text = ...),不需要再手动 dispatch_async(dispatch_get_main_queue())。

3. 基于配置 + 自定义代理(全功能模式)

这是最强大、最复杂的模式

复制代码
NSURLSession *session = [NSURLSession sessionWithConfiguration:config 
                                                      delegate:self 
                                                 delegateQueue:[NSOperationQueue mainQueue]];
  • 适用场景 :大文件下载(需要进度条)、大文件上传、需要处理 HTTPS 证书校验(didReceiveChallenge)、需要拦截服务器重定向。
  • 特点 :可以实时接收到 NSURLSessionDelegate、NSURLSessionDownloadDelegate 等协议中的回调。
  • 内存管理:会话对象会强引用(retain)代理对象 self 。如果 self 是 ViewController,这就会造成循环引用 ,导致页面 dealloc 不被调用,造成内存泄漏。

生成任务

任务的类型
任务类型 创建方法前缀 核心特征 适用场景
NSURLSessionDataTask dataTaskWith... 数据一次性加载到 内存(NSData) 中返回。 调用 JSON 接口、获取图片二进制数据(小图)。
NSURLSessionUploadTask uploadTaskWith... 专门负责把数据发送给服务器,支持后台上传。 提交头像、上传日志文件、表单提交。
NSURLSessionDownloadTask downloadTaskWith... 数据直接写入磁盘临时文件(NSURL),不占用 App 内存。支持断点续传。 下载视频、更新包、大体积 ZIP 文件。
NSURLSessionStreamTask streamTaskWith... 建立 TCP/IP 流式连接,读写数据按流处理。 即时通讯(IM)、WebSocket 长连接。

笔者目前还处于初始学习过程, 只接触到 NSURLSessionDataTask

任务的回调方式

闭包(Block) 和 代理 (delegate)

回调方式 方法特征 优点 缺点 / 限制
Block 闭包 (Completion Handler) 方法名末尾带 completionHandler: 参数。 代码紧凑 ,发起和处理的逻辑写在一起,易于阅读。 无循环引用风险(若会话无代理,则无需关心内存泄漏)。 无法获取中间进度(DownloadTask 除外,它有单独的进度闭包)。 无法处理服务器要求客户端证书(Authentication Challenge)。 无法拦截重定向。
Delegate 代理 (响应协议方法) 创建会话时传入 delegate:self,并实现 <NSURLSessionDelegate> 等协议。 精细控制 :可以实时监听 didWriteData(进度)、didReceiveChallenge(证书校验)、willPerformHTTPRedirection(重定向)。 分片接收数据 (在 didReceiveData 中手动拼接)。 代码分散 ,逻辑割裂在多个回调方法中。 极易造成循环引用 ,必须手动调用 invalidateAndCancel 断开强引用。
任务的创建方式
输入方式 方法名称特征 能否设置 Method 能否设置 Headers/Body 适用场景
传入 NSURL dataTaskWithURL: downloadTaskWithURL: 固定 GET 不能 简单的 GET 请求,无需携带 Token 或特殊头部。
传入 NSURLRequest dataTaskWithRequest: uploadTaskWithRequest: 自由设置(POST/PUT/DELETE) 能(通过 setValue:forHTTPHeaderField: 和 HTTPBody) 需要登录态(Authorization)、需要传 JSON Body 的 POST 请求。
objc 复制代码
    // 创建请求地址
    NSString* str = @"https://your-api.com/login?username=testuser&password=123456";
    NSURL* url = [NSURL URLWithString: str];
    
    // 创建请求类
    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL: url];
    // HTTP 方法
    request.HTTPMethod = @"GET";
    // 时间阈值
    request.timeoutInterval = 15;
    
    // 配置会话
        // 使用默认配置
    NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
    // 创建会话
    NSURLSession* session = [NSURLSession sessionWithConfiguration: config];

        // 通过 Request 创建
    NSURLSessionDataTask* task = [session dataTaskWithRequest: request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                if (error) {
            NSLog(@"请求失败: %@", error);
        } else {
            NSError* jsonError = nil;
            // 调用苹果原生 API 将服务器返回的二进制数据 (NSData) 转换为 Objective-C对象
            // 传入错误指针的地址, 如果二进制数据不是合法的 JSON 格式(比如缺了一个引号,或者是 HTML 报错页),jsonError 就会被赋值为一个包含具体错误原因的对象,而方法本身会返回 nil。
            id dict = [NSJSONSerialization JSONObjectWithData: data options:0 error: &jsonError];
            if (jsonError) {
                NSLog(@"JSON 解析失败");
            } else {
                NSLog(@"%@", dict);
            }
        }
    }];
//启动任务
[dataTask resume];

这是通过 requset 和 闭包 (Block) 回调的方式创建task, 还可以采用一个 [session dataTaskWithURL:url completionHandler:]的方式去创建一个,但是如果以后面的方式去创建的话,就意味着是以一种GET方式去获取网络请求,这样只能满足一些简单的网络请求,而使用 request 方式来获取数据可以满足更复杂的情况。

所以我们尽量采用request方法来获取网络请求,采用request的方式可以满足更多需求

下面是通过 URL 创建任务

objc 复制代码
// 创建地址
NSURL *url = [NSURL URLWithString:@"https://api.example.com/public/data"];

// 配置会话
        // 使用默认配置
    NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
    // 创建会话
    NSURLSession* session = [NSURLSession sessionWithConfiguration: config
                                                          delegate: nil
                                                     delegateQueue: [NSOperationQueue mainQueue]];

// 创建任务(直接传入 URL)
NSURLSessionDataTask *task = [session dataTaskWithURL:url 
                                    completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (error) {
        NSLog(@"请求失败: %@", error);
    } else {
        NSError* jsonError = nil;
        // 调用苹果原生 API 将服务器返回的二进制数据 (NSData) 转换为 Objective-C对象
        // 传入错误指针的地址, 如果二进制数据不是合法的 JSON 格式(比如缺了一个引号,或者是 HTML 报错页),jsonError 就会被赋值为一个包含具体错误原因的对象,而方法本身会返回 nil。
        id dict = [NSJSONSerialization JSONObjectWithData: data options:0 error: &jsonError];
        if (jsonError) {
            NSLog(@"JSON 解析失败");
        } else {
            NSLog(@"%@", dict);
        }
    }
}];

//  启动任务
[task resume];

通过URL创建任务就不用显式的去创建请求类

启动任务

启动任务的唯一正确方法: [task resume]

刚刚创建好的任务, 处于 suspended (挂起) 的状态 , 它虽然存在, 但是不会发出任何网络请求

相关推荐
行思理8 小时前
go语言学习难度及适合学习的人群
开发语言·学习·golang
青瓦梦滋8 小时前
WebSocket协议
linux·服务器·网络·c++·websocket·网络协议·http
zhang133830890759 小时前
传统机械三杯式风速传感器易磨损冻堵?CG-88金属超声波风速风向传感器给出免运维方案
运维·服务器·网络·人工智能·自动化
知已难寻9 小时前
第 2 篇 · 云的地基:服务器、存储、网络三大基础设施
运维·服务器·网络·云原生·云计算·负载均衡·1024程序员节
IpdataCloud11 小时前
批量查IP的API接口怎么选?免费API配额瓶颈与离线库本地批量方案实测
网络·网络协议·tcp/ip
楚山之石12 小时前
【Medical Image Analysis】深度学习医学图像合成综述:伪CT、合成MRI与合成PET的架构、性能与临床边界
图像处理·人工智能·学习·mr·mri
aiopencode12 小时前
局域网设备抓包怎么做?用 TraceEagle 抓电视、游戏机和 IoT 流量
后端·ios
ITyunwei098712 小时前
售后夜间值守怎么落地:从告警到交接的工单系统配置笔记
网络
Li_RuiQi13 小时前
DAgger方法演进_独立学习笔记
笔记·学习·计算机视觉
Smoothcloud润云13 小时前
GPU远程纳管安全吗?最小权限、网络边界与日志审计实战
服务器·网络·人工智能·安全·gpu算力·gpu