【AFNetWorking源码一】

news2024/11/27 4:29:32

文章目录

  • 前言
  • 一.原生的网络请求发送方式
    • 1.1 原生GET
    • 1.2 原生PSOT
  • 二.AFN
    • 2.1 AFN的基本架构分析
    • 2.2 以GET为例分析AFN使用流程(AFHTTPSessionManager
      • 2.2.1 AFN如何生生成对应的sessionManager
        • 2.2.2. AFURLSessionManager的初始化
        • 2.2.3 task的三种代理
          • 2.2.3.1 setDelegate方法
        • 2.2.4 AFN如何生生成对应的sessionManager总结
    • 2.3 Mananger调用GET方法的流程解析
      • 2.3.1 GET方法的执行过程
          • 生成dataTask方法
    • 在这个方法里面我们需要查看两个方法,我已经标注出来了。
    • requestWithMethod:进行序列化处理
    • dataTaskWithRequest:生成一个datatask任务
    • completionHandler
    • 总结

前言

源码库第二篇。

AFNetworking这个框架是iOS必备的一个第三方框架,其高效简洁的API使其成为最好的iOS网络请求框架,也让iOS开发的网络请求轻松许多,对于原生的GET和POST请求,AFNetworking这个框架提供了更方便的方法帮助了客户端和服务端的交互。

本次学习是基于AFNetworking4.0库,相比于3.0添加了请求头,因为在写项目的时候也发现不论是自己后端或者原生的后台API都需要写请求头这个东西。

请求头(Request Header)是在HTTP请求中包含的一部分信息,用于向服务器传递关于请求的额外信息和参数。

一.原生的网络请求发送方式

由于NSURLConnection已经被废弃,这里讲NSURLSession。从GET和POST两种请求方式入手。

1.1 原生GET

步骤

  • 确定请求路径
  • 创建可变的请求对象request:可省略((默认包含了请求头和请求方法【Get】),此步骤可以省略).
  • 创建会话session对象
  • 根据会话对象创建请求任务datatask,(利用dataTaskWithRequest或者dataTaskWithURL方法来进行创建)
  • 执行Task任务
  • 当得到服务器返回的响应后,解析数据(XML、JSON、HTTP)如果返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理

1.2 原生PSOT

  • 对于原生的POST步骤,相比于GET多了一个请求头,并且原生POST不能省略request对象,因为需要在request添加请求头
  • 确定请求的路径URL
  • 创建可变的请求对象request:不可省略——POST请求需要设置请求头
  • 设置请求头(需要对创建好的请求头进行序列化:确保为服务端可识别的数据格式)和请求方式POST
  • 创建会话session对象
  • 根据会话对象创建请求任务datatask——(通过dataTaskWithRequest)
  • 执行Task
  • 当得到服务器返回的响应后,解析数据(XML、JSON、HTTP)如果返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理、

二.AFN

2.1 AFN的基本架构分析

请添加图片描述

AFN4.0和3.0的基本架构变化不大,整体分为图上的文件部分

  • NSURLSession:网络请求的主要类,AFURLSessionManager封装的是NSURLSession,而AFHTTPSessionManager是其子类,用于HTTP请求做了一些优化
  • Reachability:网络状况,AFNetworkReachabilityManager是用来监测当前网络状况的一个类
  • Security:网络安全,HTTPS请求就要用到AFSecurityPolicy
  • Serialization:序列化,AFURLRequestSerialization是请求前的序列化,AFURLResponseSerialization是请求完成后对结果的序列化
  • UIKit:里面则是一些UIkit的拓展Category

类和类的关系如下图所示
请添加图片描述
AFN的模块划分

  • 通过使用和上述图解可以看出AFN的整体就是围绕着AFURLSessionManager来进行的。
  • AFNetWorking大致分为一下五大模块,其中最为核心的是网络通信模块,主要用来发送和响应请求。
  • AFNetworkReachabilityManager主要是用来进行网络状态的监听,在不同网络状态下请求和响应的不同操作和处理;
    为了保证网络请求的安全性,当然少不了AFSecurityPolicy,网络安全策略模块,在初始化sessionmanger的时候同时初始化了AFSecurityPolicy以及网络通信信息请求序列化的类
  • 网络通信信息序列化模块分为请求序列化和响应序列化,其内部分别会对请求头和响应头进行编码,在请求的时候,将请求头转码为计算机可识别的格式,在响应的时候将响应结果进行转码后传回给客户端
  • UIKit库的一些相关拓展,其中包括UIImageView的请求,UIButton的请求等等。

整个步骤也可以理解为下图
在这里插入图片描述

  • 网络通信模块(AFURLSessionManager、AFHTTPSessionManager)
  • 网络状态监听模块(AFNetworkReachabilityManager)
  • 网络通信安全策略模块(AFSecurityPolicy)
  • 网络通信信息序列化模块(AFURLRequestSerialization,AFURLResponseSerialization)
  • iOS UIkit库的拓展(UIKit)

2.2 以GET为例分析AFN使用流程(AFHTTPSessionManager

以GET请求为例,以下是请求的代码。

  • 生成对应的sessionManager;
  • 调用GET方法进行请求;
  • GET回调里边处理结果,包括(任务进度,请求成功的结果,请求失败的结果);

请添加图片描述

2.2.1 AFN如何生生成对应的sessionManager

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

跟着图解一步一步看
在这里插入图片描述

首先初始化,然后一步一步调用到父类的方法,我们分段看。
在这里插入图片描述

  • 初始化,调用initWithBaseURL
+ (instancetype)manager {
    return [[[self class] alloc] initWithBaseURL:nil];
}
  • initWithBaseURL调用initWithBaseURL:(NSURL *)url sessionConfiguration:
- (instancetype)initWithBaseURL:(NSURL *)url {
    return [self initWithBaseURL:url sessionConfiguration:nil];
}
  • initWithBaseURL:(NSURL *)url sessionConfiguration: 实现分析
- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
// 最终都会调用父类的方法去实现初始化
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
    
    //条件判断了URL的路径是否具有长度大于0。它使用了[url path]方法来获取URL的路径,并通过length方法获取路径的长度。
    // [[url absoluteString] hasSuffix:@"/"]:这个条件判断了URL的绝对字符串表示是否以斜杠("/")结尾。它使用了[url absoluteString]方法获取URL的绝对字符串表示,并通过hasSuffix:方法检查字符串是否以斜杠结尾。
    
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {

// 如果上述两个条件都为真(路径长度大于0且不以斜杠结尾),则执行以下代码块:
// url = [url URLByAppendingPathComponent:@""];:这行代码使用URLByAppendingPathComponent:方法将一个空路径组件追加到原始URL的路径末尾,从而确保URL的路径以斜杠结尾。

        url = [url URLByAppendingPathComponent:@""];
    }

    self.baseURL = url;

    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}
  • 总体而言,这段代码的作用是检查URL的路径是否为空或不以斜杠结尾,如果不符合条件,则在路径末尾追加一个空路径组件,以确保URL的路径以斜杠结尾。

2.2.2. AFURLSessionManager的初始化

从上述的最后调用 self = [super initWithSessionConfiguration:configuration];能看出来在最后AFHTTPSessionManager中的初始化方法最终都会调用其父类的initWitchSessionConfiguration初始化方法,返回一个sessionManager方法;那么,需要去看一下父类也就是AFURLSessionManager的初始化都做了什么

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
   
    if (!configuration) {
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        /*
         NSURLSessionConfiguration是用于配置NSURLSession的类。它提供了一系列属性和方法,用于定制和管理会话(session)的行为和特性。
         
         defaultSessionConfiguration:返回一个默认的会话配置,可用于创建默认的NSURLSession实例。
         */
    }

    self.sessionConfiguration = configuration;
    
    /// 初始化操作队列 并设置为串行队列 设置最大并发操作数
    self.operationQueue = [[NSOperationQueue alloc] init];
    
    /// ‼️队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数。
    /// ‼️在多线程编程中,操作队列(operation queue)是用于管理操作(operation)的一种机制。操作队列可以用来异步执行一系列任务,并控制它们的并发性。
    /// 通过将最大并发操作数设置为 1,即 maxConcurrentOperationCount = 1,可以确保操作队列中的操作按顺序依次执行,而不会并发执行。这意味着每个操作将在上一个操作完成后才会开始执行。
    self.operationQueue.maxConcurrentOperationCount = 1;

    /// AFJSONResponseSerializer 用来序列化HTTP的响应
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    /// 初始化SSI需要的 AFSecurityPolocy用来保证请求的安全性
    self.securityPolicy = [AFSecurityPolicy defaultPolicy];

#if !TARGET_OS_WATCH
    /// AFNetWorkRarchability Manager 查看网络连接情况
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif

    /// ‼️初始化可变任务字典 任务task的id作为key 代理对象作为value
    /// self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

    // 锁的初始化并命名
    self.lock = [[NSLock alloc] init];
    self.lock.name = AFURLSessionManagerLockName;

    /// ‼️ 在初始化的时候获取当前session中的所有task,为它们重新设置一遍代理;一般来说初始化的session中的task应该是为空的,这里这么做的主要目的是为了防止从后台回来的时候初始化session,对于一些后台之前的请求任务没有重设代理导致崩溃的问题
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];

    return self;
}

  • 初始化当前的会话配置,操作队列,锁,AFNetworkReachabilityManagerAFSecurityPolicy,请求序列化以及用来存储任务的可变任务字典等属性;
  • 获取当前session中所有未完成的task,给它们设置一遍代理;
  • 需要注意‼️的地方:
    • self.mutableTaskDelegatesKeyedByTaskIdentifier 是一个可变的字典属性,用于将任务标识符(task identifier)与相应的任务委托对象进行关联
    • ‼️在多线程编程中,操作队列(operation queue)是用于管理操作(operation)的一种机制。操作队列可以用来异步执行一系列任务,并控制它们的并发性。
    • 通过将最大并发操作数设置为 1,即 maxConcurrentOperationCount = 1,可以确保操作队列中的操作按顺序依次执行,而不会并发执行。这意味着每个操作将在上一个操作完成后才会开始执行。

2.2.3 task的三种代理

对于不同的任务,AFN是如何完成网络请求,通过getTasksWithCompletionHandler: 方法来获取当前会话(session)中的所有任务,并对每个任务添加相应的任务委托对象。
请添加图片描述
代码的执行流程如下:

  • 调用 getTasksWithCompletionHandler: 方法,该方法接受一个完成处理程序块作为参数。
  • 在完成处理程序块中,会收到三个参数 dataTasksuploadTasksdownloadTasks,它们分别表示当前会话中的数据任务、上传任务和下载任务。
  • 使用 for-in 循环遍历 dataTasks 数组,对每个 NSURLSessionDataTask 类型的任务执行以下操作:
    • 调用 addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法,将任务和相应的参数传递给该方法。
    • 该方法将为数据任务添加一个任务委托对象,并根据需要设置上传和下载进度的回调以及任务完成的回调。
  • 使用 for-in 循环遍历 uploadTasks 数组,对每个 NSURLSessionUploadTask 类型的任务执行类似的操作:
    • 调用 addDelegateForUploadTask:progress:completionHandler: 方法,将任务和相应的参数传递给该方法。
    • 该方法将为上传任务添加一个任务委托对象,并根据需要设置上传进度的回调以及任务完成的回调。
  • 使用 for-in 循环遍历 downloadTasks 数组,对每个 NSURLSessionDownloadTask 类型的任务执行类似的操作:
    • 调用 addDelegateForDownloadTask:progress:destination:completionHandler: 方法,将任务和相应的参数传递给该方法。
    • 该方法将为下载任务添加一个任务委托对象,并根据需要设置下载进度、文件保存路径的回调以及任务完成的回调。

通过getTasksWithCompletionHandler方法,可以将适当的任务委托对象与每个任务关联起来,并对任务的执行过程进行跟踪和处理。

2.2.3.1 setDelegate方法

查看这三个代理方法的源码请添加图片描述请添加图片描述
请添加图片描述
通过观察三个代理方法的实现,到最后都会调用setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task

方法 setDelegate:forTask: 的实现,用于为给定的 NSURLSessionTask 设置相应的 AFURLSessionManagerTaskDelegate 任务委托对象。

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
/// 首先,通过使用 NSParameterAssert 宏进行断言,确保 task 和 delegate 参数非空。这是一种防御性编程的做法,以确保传入的参数符合预期,避免在后续使用过程中出现问题。
    NSParameterAssert(task);
    NSParameterAssert(delegate);
/// 接下来,通过调用 [self.lock lock] 来获取一个锁,确保在多线程环境中对 self.mutableTaskDelegatesKeyedByTaskIdentifier 字典的操作是线程安全的。
    [self.lock lock];
/// 将 delegate 任务委托对象与 task.taskIdentifier 任务标识符关联,并将其存储在 self.mutableTaskDelegatesKeyedByTaskIdentifier 字典中。使用 @(task.taskIdentifier) 将任务标识符转换为 NSNumber 对象作为字典的键。
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
/// 调用 [self addNotificationObserverForTask:task] 方法,为任务添加通知观察者,以便在任务执行过程中获取相关通知,如任务完成、进度更新等。
    [self addNotificationObserverForTask:task];
/// 最后,通过调用 [self.lock unlock] 释放锁,确保线程安全的访问完成。
    [self.lock unlock];
}

该方法的作用是将指定的任务委托对象与任务关联起来,并将其存储在字典中以便后续使用。这样可以轻松地追踪和管理与每个任务相关的委托对象,以便进行任务状态跟踪、结果处理或其他相关操作

2.2.4 AFN如何生生成对应的sessionManager总结

至此,对于初始化的时候获取当前session中的所有task,已经为它们重新设置一遍代理。回到initWitchSessionConfiguration方法中返回当前对象,向上返回,生成AFHTTPSessionManager *sessionManger对象;

2.3 Mananger调用GET方法的流程解析

还是以GET为例子,先看图解
在这里插入图片描述

2.3.1 GET方法的执行过程

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(nullable id)parameters
                      headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                     progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
                      success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                      failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
    /// 调用 dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure: 方法,该方法创建一个 NSURLSessionDataTask 数据任务对象,用于执行指定的 HTTP GET 请求。将传入的参数传递给该方法以构建请求。

    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                          headers:headers
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    
    /// 调用 [dataTask resume] 方法启动数据任务,使其开始执行。

    [dataTask resume];
    
    /// 返回数据任务对象 dataTask。
    return dataTask;
参数:
URLString:请求的URL值
parameters:根据需求的请求参数
headers:请求头
downloadProgress:更新下载进度的对象
success:任务成功后执行的Block对象
failure:任务失败后执行的Block对象
}

代码的执行流程如下:

  • 接收传入的 URLStringparametersheadersdownloadProgresssuccessfailure 参数,它们分别表示请求的 URL 字符串、请求参数、请求头、下载进度回调、成功回调和失败回调。
  • 调用 dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure: 方法,该方法创建一个 NSURLSessionDataTask 数据任务对象,用于执行指定的 HTTP GET 请求。将传入的参数传递给该方法以构建请求。
  • 调用 [dataTask resume] 方法启动数据任务,使其开始执行。
  • 返回数据任务对象 dataTask

通过这段代码,可以方便地发起一个 GET 请求,并且可以通过传入的参数来定制请求的 URL、参数、请求头等。同时,可以指定下载进度回调、成功回调和失败回调来处理请求的结果和状态。最后,代码中的 [dataTask resume] 启动任务,使其开始执行。

生成dataTask方法
  • dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure: 该方法是生成datatask,是AFHTTPSessionManager中的da taTaskWithHTTPMethod方法
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(nullable id)parameters
                                         headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                         failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
    NSError *serializationError = nil;
    
    /// 使用 `self.requestSerializer` 对象根据传入的参数构建一个 `NSMutableURLRequest` 可变请求对象。
    /// self.requestSerializer:请求序列化器
    /// ‼️‼️ 需要点进去的方法1
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    for (NSString *headerField in headers.keyEnumerator) {
        [request setValue:headers[headerField] forHTTPHeaderField:headerField];
    }

///  // 如果在构建请求过程中出现了 `serializationError` 错误,即请求参数序列化错误,则会执行相应的错误处理逻辑。
    if (serializationError) {
        if (failure) {
       /// 如果存在 `failure` 失败回调,则将错误通过异步方式回调到主队列上。
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

        return nil;
    }
/// 创建一个 `NSURLSessionDataTask` 数据任务对象
    __block NSURLSessionDataTask *dataTask = nil;
	/// 并调用 `dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:` 方法来配置任务的上传进度回调、下载进度回调和完成处理程序块。
	// ‼️ 需要点进去的方法2
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}

dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure: 方法的实现。该方法用于创建一个 NSURLSessionDataTask 数据任务对象,并配置请求的相关参数。

代码的执行流程如下:

  • 接收传入的 methodURLStringparametersheadersuploadProgressdownloadProgresssuccessfailure 参数,它们分别表示请求的 HTTP 方法、URL 字符串、请求参数、请求头、上传进度回调、下载进度回调、成功回调和失败回调。
  • 使用 self.requestSerializer 对象根据传入的参数构建一个 NSMutableURLRequest 可变请求对象。请求的 URL 会根据 URLStringself.baseURL 进行拼接和转换。请求的参数和错误对象会由 requestWithMethod:URLString:parameters:error: 方法返回。
  • 遍历 headers 字典中的键值对,将键作为请求头字段,值作为请求头的值,通过 setValue:forHTTPHeaderField: 方法设置到请求对象中。
  • 如果在构建请求过程中出现了 serializationError 错误,即请求参数序列化错误,则会执行相应的错误处理逻辑。如果存在 failure 失败回调,则将错误通过异步方式回调到主队列上。
  • 创建一个 NSURLSessionDataTask 数据任务对象,并调用 dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: 方法来配置任务的上传进度回调、下载进度回调和完成处理程序块。
  • 返回数据任务对象 dataTask

在这个方法里面我们需要查看两个方法,我已经标注出来了。

  • 一个是requestWithMethod序列化处理
    • 序列化处理的原因:在HTTp网络请求是基于字节流的网络传输,序列化是可以将一个对象转化成一段字节编码,以此方便在网络上传输或者做其他存储处理,使用的时候在对其做反序列化;简单的来说就是为了统一,我们可以用自己的方法来保存对象,但是在底层只提供一种保存对象状态的机制。因此我们在存储的时候需要进行序列化,以此来和提供的保持一致,在读取的时候对其进行反序列化,得到我们想要的形式;
  • 一个是dataTaskWithRequest生成数据任务

requestWithMethod:进行序列化处理

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
/// 首先进行参数断言,确保传入的 method 和 URLString 不为 nil
    NSParameterAssert(method);
    NSParameterAssert(URLString);
/// 使用 NSURL 的 URLWithString: 方法将 URLString 转换为 NSURL 对象,然后进行参数断言,确保 url 不为 nil。
    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);
/// 创建一个可变的 NSMutableURLRequest 请求对象,使用 initWithURL: 方法将 url 作为初始化参数,并将 method 赋值给请求对象的 HTTPMethod 属性。
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;
///遍历 AFHTTPRequestSerializerObservedKeyPaths() 方法返回的被观察的键路径,通过ketPath检查 self.mutableObservedChangedKeyPaths 是否包含该键路径,如果包含,则将 self 对象的对应键路径的值设置为请求对象的对应键路径的值。存到mutableRequest中
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

/// 调用 requestBySerializingRequest:withParameters:error: 方法,将请求对象和参数进行序列化,返回一个经过序列化处理的请求对象。如果出现错误,会将错误对象赋值给传入的 error 参数。

    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
    ///mutableCopy: 将序列化后的请求对象转换为可变的副本,并返回该对象。

	return mutableRequest;
}

dataTaskWithRequest:生成一个datatask任务

- (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 {

/// 接收一个 NSURLRequest 对象作为请求参数,并使用该请求对象创建一个 NSURLSessionDataTask 对象,即 dataTask。
    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];

/// 调用 self 对象的 addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法,为 dataTask 添加代理、上传进度回调、下载进度回调和完成处理块。这些参数分别是 uploadProgressBlock、downloadProgressBlock 和 completionHandler。
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

AFURLSessionManager 类的 dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: 方法的实现。该方法用于创建一个 NSURLSessionDataTask 对象,并为该任务添加代理和相关的进度回调以及完成处理块。

completionHandler

请添加图片描述

  • completionHandler是一个回调闭包,用于处理网络请求完成后返回的结果。它的定义为 (nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))
  • 其中 response 是请求的响应对象,responseObject 是响应体数据(可以是NSData、NSDictionary等),error 是请求过程中出现的错误信息,如网络不可用、请求超时等。
  • dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler: 中,当网络请求完成后,会调用这个闭包处理请求结果。

总结

至此做完上述处理之后,最终将生成的dataTask返回到GET方法当中,这样最终在GET中就拿到了我们可以发送请求的task,最后调用系统方法[dataTask resume];发起网络请求;

对于AFURLSessionManager还有他遵守的很多代理是在我们进行网络请求的时候调用的,接下来学习AFURLSessionManager及其代理解析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/538216.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣sql中等篇练习(二十二)

力扣sql中等篇练习(二十二) 1 坚定的友谊 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below # ①求出所有朋友关系的总表 WITH T as (SELECT user1_id ,user2_idFROM friendshipUNION SELECT user2_id user1…

前端学习--Vue(1)webpack

前端工程化 模块化&#xff08;js、css模块化、资源模块化&#xff09; 组件化&#xff08;复用现有的UI结构、样式、行为&#xff09; 规范化&#xff08;目录结构划分、编码规范化、接口文档规范化、git分支管理&#xff09; 自动化&#xff08;自动化构建、自动部署、自动化…

试用阿里云云拨测对Web网站的网页性能进行测试并分析

目录 前言 云拨测操作步骤 1.开通应用实时监控服务ARMS 2.成功登录后&#xff0c;在产品类别下选择中间件>应用实时监控服务&#xff0c;在云拨测卡片上单击立即试用。 3.登录ARMS控制台&#xff0c;在左侧导航栏中选择云拨测>定时拨测&#xff0c;在定时拨测页面右上…

chatgpt赋能Python-pycharm怎么与python关联

Pycharm怎么与Python关联&#xff1f; 介绍 对于Python开发人员来说&#xff0c;Pycharm是一个非常强大的IDE。它提供了各种工具和功能&#xff0c;用于快速开发和调试Python代码。但是&#xff0c;在开始开发之前&#xff0c;必须将Pycharm与Python关联起来。否则&#xff0…

chatgpt赋能Python-pulp_python

Pulp Python: 介绍与优点 在数据分析和决策优化领域&#xff0c;线性规划是一种非常有效的数学模型。而Pulp Python是一款优秀的线性规划库&#xff0c;它可以帮助用户快速进行线性规划求解&#xff0c;同时还具备可扩展性和易使用性等优点。 什么是线性规划&#xff1f; 线…

chatgpt赋能Python-pycharm_滚轮字体大小设置

Pycharm滚轮字体大小设置 Pycharm是一款非常流行的Python IDE&#xff0c;广泛用于Python编程。然而&#xff0c;每个开发者都有其自己的偏好设置&#xff0c;包括字体大小。此文将介绍如何使用滚轮控制Pycharm中编辑器的字体大小。 界面操作 在Pycharm中&#xff0c;您可以…

南京邮电大学Web技术双语实验二(Web服务端脚本编写)

文章目录 一、 实验目的和要求二、实验环境(实验设备)三、实验原理及内容1首先编写html页面2 html页面效果如下图所示3 编写服务端php脚本4 服务端页面显示如下 四、实验小结&#xff08;包括问题和解决方法、心得体会、意见与建议等&#xff09; 一、 实验目的和要求 1 显示一…

进程通信 — 管道

目录 进程通信前提进程间通信的目的进程间通信发展进程间通信分类管道认识管道匿名管道站在文件描述符角度&#xff0c;深度理解管道 管道读取规则读写规则 管道特点命名管道命名管道的打开规则 匿名管道与命名管道的区别命名管道的打开规则 匿名管道与命名管道的区别 进程通信…

chatgpt赋能Python-pycharm怎么关联python

PyCharm怎么关联Python PyCharm是一款非常流行的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;它经常被Python开发者用来编写、调试和测试他们的代码。为了让PyCharm正常工作&#xff0c;需要将其与Python相关联。在这篇文章中&#xff0c;我们将讨论如何在PyCh…

最小二乘法

本文结合对ChatGTP的提问&#xff0c;用自己的理解讲一讲最小二乘法。 最小二乘法&#xff1a; yi是实际值&#xff0c;yhat是理论值&#xff0c;就是拟合值&#xff0c;比方说一次函数做拟合&#xff0c;那就是在这个x点位置时的值。累加所有yi-yhat的平方&#xff0c;得到E并…

chatgpt赋能Python-pycharm怎么下载python

PyCharm怎么下载Python&#xff1f; PyCharm是一款非常流行的Python开发IDE&#xff0c;Python是一门流行的编程语言。所以&#xff0c;对于那些想要使用PyCharm进行Python编程的人们来说&#xff0c;下载Python是一个关键的步骤。 什么是Python&#xff1f; Python是一门流…

消息中间件概念

什么是消息队列 消息队列是消息中间件的一种实现方式。 什么是消息中间件&#xff1f; 将消息中间件需要理解一下什么是消息和中间件&#xff1f; 消息 消息是指软件对象之间进行交互作用和通讯利用的一种方式。 中间件 百度百科的介绍&#xff1a; ​ 中间件&#xff08;Mid…

改进YOLOv5 | 在 C3 模块中添加【Triplet】【SpatialGroupEnhance】【NAM】【S2】注意力机制 | 附详细结构图

文章目录 1. Triplet 注意力模块1.1 原理1.2 C3_Triplet 代码2. SpatialGroupEnhance 注意力模块2.1 原理2.2 C3_SpatialGroupEnhance 代码3. NAM 注意力模块3.1 原理3.2 C3_NAM 代码4. S2 注意力模块4.1 原理4.2 C3_S2 代码5. 添加方式💡6. C3_Attention 结构图 &#x

chatgpt赋能Python-pycharm怎么编译python

PyCharm怎么编译Python&#xff1f; Python是当今最流行的编程语言之一&#xff0c;其简单易学、高效实用的特性使其受到了广大开发者的欢迎。PyCharm是一款专业的Python集成开发环境&#xff0c;它提供了许多有用的功能和工具&#xff0c;可以极大地提高开发效率。本文将介绍…

C++ 端点检测(8领域检测)

直线曲线端点检测 一、代码二、结果适用于直线、曲线等线的端点检测。 方法是:通过判断一个点周围8个点的像素的值来判断该点是否为端点,若周围8个点中有且只有一个点像素不为0,则认为该点是线段上的一个端点。 一、代码 #include <opencv2/opencv.hpp> using name…

lwip的路由表

在lwIP中&#xff0c;路由表被实现为一个以链表形式组织的数据结构&#xff0c;每个表项都包含一个目的网络地址和一个指向输出接口的指针。当需要将数据包发送到某个目的地址时&#xff0c;lwIP会遍历路由表&#xff0c;并找到最佳匹配的表项&#xff0c;然后将数据包发送到相…

《Linux0.11源码解读》理解(三) 执行setup

上一节的最后jmpi把cs:ip设置为0x9020:0000。于是CPU开始执行setup&#xff0c;它的作用是获取机器系统数据至内存&#xff0c;关中断并挪动system&#xff0c;为32位模式转换做准备。 加载系统信息至内存 同样是调用BISO中断&#xff0c;寄存器作为入参和返回值&#xff0c;…

javaweb系列-Vue.js

w2.1 Vue概述 一个完整的html页面包括了视图和数据&#xff0c;数据是通过请求 从后台获取的&#xff0c;那么意味着我们需要将后台获取到的数据呈现到页面上&#xff0c;很明显&#xff0c; 这就需要我们使用DOM操作。正因为这种开发流程&#xff0c;所以我们引入了一种叫做M…

NLP大模型微调答疑

什么情况用Bert模型&#xff0c;什么情况用LLaMA、ChatGLM类大模型&#xff0c;咋选&#xff1f; 答&#xff1a;Bert 的模型由多层双向的Transformer编码器组成&#xff0c;由12层组成&#xff0c;768隐藏单元&#xff0c;12个head&#xff0c;总参数量110M&#xff0c;约1.1…

虚拟机02 mysql安装和配置

第一步&#xff1a;在课程资料中&#xff0c;找到数据库安装文件&#xff0c;然后上传安装文件到Linux中。文件位置如图所示&#xff1a; 第二步&#xff1a;打开Linux&#xff0c;到工作目录中创建一个mysql目录&#xff0c;然后将安装文件上传到mysql中 第三步&#xff1a…