系列文章目录
iOS基础—Block
iOS基础—Protocol
iOS基础—KVC vs KVO
iOS网络—AFNetworking
iOS网络—NSURLSession
iOS内存管理—MRC vs ARC
iOS基础—Category vs Extension
iOS基础—多线程:GCD、NSThread、NSOperation
iOS基础—常用三方库:Masonry、SDWebImage
iOS基础—定时器:GCD、NSTimer、CADisplayLink
文章目录
- 系列文章目录
- 一、和NSURLSession的关系
- 二、AFNetworking的架构
- 三、AFURLSessionManager解析
- 1.持有属性
- 2.初始化
- 3.进度管理
- 4.通知转发
- 四、AFHTTPSessionManager解析
- 五、使用示例
一、和NSURLSession的关系
AFNetworking 是一个基于 NSURLSession 和 NSURLConnection 的强大网络库,旨在简化 iOS 和 macOS 应用中的网络请求和响应处理。NSURLSession 是 Apple 提供的基础网络库,而 AFNetworking 则在其基础上进行了封装,提供了更高层次的抽象和更多的功能。
NSURLSession简介:
NSURLSession 是 Apple 提供的基础网络库,用于处理 HTTP/HTTPS 请求。它提供了以下功能:
- 数据任务 (NSURLSessionDataTask)
- 下载任务 (NSURLSessionDownloadTask)
- 上传任务 (NSURLSessionUploadTask)
- 支持后台下载和上传
- 支持自定义会话配置 (NSURLSessionConfiguration)
- 支持代理 (NSURLSessionDelegate)
AFNetworking (github🔗)简介:
AFNetworking 是一个基于 NSURLSession 的第三方网络库(3.0版本中的 NSURLConnection 已被全部弃用),提供了更高层次的抽象和更多的功能。主要特点包括:
- 简化网络请求的创建和管理
- 支持 JSON、XML、Plist 等多种数据格式解析
- 内置图片下载和缓存功能
- 提供网络状态监控
- 支持安全的 HTTPS 请求
- 支持后台下载和上传
- 提供了更灵活的请求和响应处理机制
AFNetworking 与 NSURLSession 的关系:
- 封装与扩展
AFNetworking 通过对 NSURLSession 的封装,简化了网络请求的创建和管理。它提供了更简洁的 API,使开发者可以更方便地进行网络操作。- 高层次抽象
AFNetworking 提供了更高层次的抽象,隐藏了许多底层实现细节。开发者可以通过简单的 API 发起网络请求,而无需关心底层的实现和配置。- 额外功能
AFNetworking 在 NSURLSession 的基础上,增加了许多额外的功能,如:
- 请求和响应处理:提供了更灵活的请求和响应处理机制,可以方便地进行数据解析和错误处理。
- 图片下载和缓存:内置了图片下载和缓存功能,适用于需要加载图片的应用。
- 网络状态监控:提供了网络状态监控功能,可以检测网络连接的变化。
二、AFNetworking的架构
AFNetworking 框架主要包含两部分:
- AFNetworking 核心功能
- UIKit+AFNetworking 分类功能
目录结构如下:
- 网络通信模块(AFURLSessionManager、AFHTTPSessionManger)
- 网络状态监听模块(Reachability)
- 网络通信安全策略模块(Security)
- 网络通信数据序列化/反序列化模块(Serialization)
- 对于iOS UIKit库的扩展(UIKit)
三、AFURLSessionManager解析
1.持有属性
首先来看看 AFURLSessionManager 持有的公有属性:
@interface AFURLSessionManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>/// 管理的 Session
@property (readonly, nonatomic, strong) NSURLSession *session; /// 代理回调执行的队列
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;/// 通过使用dataTaskWithRequest:success:failure:创建的数据任务,并使用GET/POST/等方便方法运行,服务器发送的响应会自动由响应序列化器进行验证和序列化。
/// 默认情况下,此属性被设置为AFJSONResponseSerializer的一个实例。
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer; /// 由创建的会话用于评估安全连接的服务器信任的安全策略。AFURLSessionManager使用defaultPolicy,除非另有指定。
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;///网络可达性管理器。AFURLSessionManager默认使用sharedManager。
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;/// 当前由托管 session 运行的数据、上传和下载任务。
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;/// The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;/// The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
私有属性如下,可以做了解:
@interface AFURLSessionManager ()
@property (readwrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration;
@property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue;
@property (readwrite, nonatomic, strong) NSURLSession *session;
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
@property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
@property (readwrite, nonatomic, strong) NSLock *lock;
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskAuthenticationChallengeBlock authenticationChallengeHandler;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics AF_API_AVAILABLE(ios(10), macosx(10.12), watchos(3), tvos(10));
#endif
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end
2.初始化
源码中 AFURLSessionManager 的初始化方法如下:
- (instancetype)init {return [self initWithSessionConfiguration:nil];
}- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {self = [super init];if (!self) {return nil;}if (!configuration) {configuration = [NSURLSessionConfiguration defaultSessionConfiguration];}self.sessionConfiguration = configuration;self.operationQueue = [[NSOperationQueue alloc] init];self.operationQueue.maxConcurrentOperationCount = 1;self.responseSerializer = [AFJSONResponseSerializer serializer];self.securityPolicy = [AFSecurityPolicy defaultPolicy];#if !TARGET_OS_WATCHself.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endifself.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];self.lock = [[NSLock alloc] init];self.lock.name = AFURLSessionManagerLockName;[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;
}
我们可以总结一下初始化流程:
- 初始化 sessionConfiguration、operationQueue、responseSerializer等属性,大部分都是 default 配置。
- 调用 getTasksWithCompletionHandler: 方法获取当前 session 中的 tasks,并为 tasks 设置代理,处理返回的数据
3.进度管理
AFURLSessionManager 作为会话对象的代理,基本可以处理所有任务对象。但是,对于上传任务、下载任务进行进度管理,使用 AFURLSessionManager 来管理是较为繁琐的,也是不合理的。主要有以下几点原因:
- 在上传任务和下载任务的整个生命周期中都需要持有一个进度对象 NSProgress。
NSProgress 是 Apple 提供的一个类,用于跟踪和报告任务的进度。它在处理长时间运行的任务(如文件下载、数据处理等)时非常有用。NSProgress 可以在多个线程之间共享进度信息,并且支持嵌套进度对象,从而提供更细粒度的进度跟踪。- 在下载任务的整个生命周期中还需要持有一个接收数据对象,用于持续接收数据。
- AFURLSessionManager 主要用于管理会话,根据职责单一原则,它不应该管理任务对象的具体细节。
因此,AFNetworking 提供了 AFURLSessionManagerTaskDelegate 这样一个内部类,用于处理 NSURLSessionTask 的代理回调。它是 AFURLSessionManager 的辅助类,负责处理任务的进度、完成、失败等事件。通过使用这个代理类,AFNetworking 可以更好地管理和处理各种网络任务。
AFURLSessionManager 内部维护了一个可变字典属性 mutableTaskDelegatesKeyedByTaskIdentifier,以任务对象的标识符 taskIdentifier 作为键,以 AFURLSessionManagerTaskDelegate 对象作为值。在初始化任务对象时,就绑定一个 AFURLSessionManagerTaskDelegate 对象,以处理进度、下载数据。
具体的代码逻辑如下:
// 根据特定 request 创建一个 NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)requestuploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlockdownloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlockcompletionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {// 创建任务对象NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];// 为任务对象设置代理对象、上传进度回调、下载进度回调、完成回调[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];return dataTask;
}// 中间调用了 addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 方法
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTaskuploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlockdownloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlockcompletionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{// 创建一个 AFURLSessionManagerTaskDelegate 对象,用以处理进度、下载数据AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];delegate.manager = self;delegate.completionHandler = completionHandler;// 用来标识任务对象所属的 SessionManagerdataTask.taskDescription = self.taskDescriptionForSessionTasks;[self setDelegate:delegate forTask:dataTask];delegate.uploadProgressBlock = uploadProgressBlock;delegate.downloadProgressBlock = downloadProgressBlock;
}// 其中又调用了 setDelegate:forTask: 方法
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegateforTask:(NSURLSessionTask *)task
{NSParameterAssert(task);NSParameterAssert(delegate);[self.lock lock];// 保存 NSURLSessionTask <-> AFURLSessionManagerTaskDelegate 的映射关系self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;[self addNotificationObserverForTask:task];[self.lock unlock];
}
AFURLSessionManagerTaskDelegate 主要实现了以下这些协议方法。在这些方法内实现进度管理、下载管理。AFURLSessionManager 虽然也实现了这些协议,但是最终还是会调用 AFURLSessionManagerTaskDelegate 中的实现。
- NSURLSessionTaskDelegate
URLSession:task:didCompleteWithError:
URLSession:task:didFinishCollectingMetrics:- NSURLSessionDataDelegate
URLSession:dataTask:didReceiveData:
URLSession:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:- NSURLSessionDownloadDelegate
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
URLSession:downloadTask:didFinishDownloadingToURL:
4.通知转发
NSURLSession 定义了一系列通知,为了提供更完美的封装,AFURLSessionManager 捕获了这些通知,将它们转换成自己所定义的通知后,再进行转发。
这里,就用到了任务对象的 taskDescription 属性。taskDescription 属性描述了该任务对象所属的 Session Manager。因为应用程序中可能会初始化多个 AFURLSessionManager。Session Manager 只有在捕获到它所管理的任务对象发出的通知后才会进行封装和转发。如:
- AFNetworkingTaskDidResumeNotification
- AFNetworkingTaskDidSuspendNotification
//当任务恢复时,AFURLSessionManager 会捕获 NSURLSessionTaskDidResumeNotification 通知,并检查任务的描述是否与 taskDescriptionForSessionTasks 匹配。
//如果匹配,则发送自定义的 AFNetworkingTaskDidResumeNotification 通知。
- (void)taskDidResume:(NSNotification *)notification {NSURLSessionTask *task = notification.object;if ([task respondsToSelector:@selector(taskDescription)]) {if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];});}}
}//当任务暂停时,AFURLSessionManager 会捕获 NSURLSessionTaskDidSuspendNotification 通知,并检查任务的描述是否与 taskDescriptionForSessionTasks 匹配。
//如果匹配,则发送自定义的 AFNetworkingTaskDidSuspendNotification 通知。
- (void)taskDidSuspend:(NSNotification *)notification {NSURLSessionTask *task = notification.object;if ([task respondsToSelector:@selector(taskDescription)]) {if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];});}}
}
四、AFHTTPSessionManager解析
我们可以先来看看 AFHTTPSessionManager 持有的公有属性。由于,AFHTTPSessionManager 主要用于 HTTP 请求,所以它为 HTTP 请求提供了一个请求序列化器和一个响应序列化器,并分别设置默认值为 HTTP 和 JSON:
//在方法例如 requestWithMethod:URLString:parameters: 和 GET / POST / 等方便方法中,用于从相对路径构造请求的 URL。
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;//使用requestWithMethod:URLString:parameters:和 multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:创建的请求
//通过使用此属性指定的参数序列化器构造默认的一组请求头。默认情况下,此属性设置为AFHTTPRequestSerializer的一个实例,它将查询字符串参数序列化为GET、HEAD和DELETE请求,或者将HTTP消息体进行URL编码。
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;//使用dataTaskWithRequest:success:failure:创建并使用GET / POST / 等方便方法运行的数据任务,从服务器发送的响应会自动由响应序列化器进行验证和序列化。
//默认情况下,此属性设置为AFJSONResponseSerializer的一个实例。
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;//由创建的会话用于评估安全连接的服务器信任的安全策略。 除非另有指定,否则AFURLSessionManager将使用defaultPolicy。
//配置了AFSSLPinningModePublicKey或AFSSLPinningModeCertificate的安全策略只能应用于使用安全基础URL(即https)初始化的会话管理器。
//在不安全的会话管理器上应用启用了插销的安全策略会引发“Invalid Security Policy”异常。
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
除此之外,AFHTTPSessionManager 还封装了 AFURLSessionManager 的一些复杂的任务对象初始化方法,并提供了一些非常简单的便利方法,用以提供各种 HTTP 请求。如下所示:
// 发送一个GET请求。
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headersprogress:(nullable void (^)(NSProgress *downloadProgress))downloadProgresssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个HEAD请求。
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headerssuccess:(nullable void (^)(NSURLSessionDataTask *task))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个POST请求。
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headersprogress:(nullable void (^)(NSProgress *uploadProgress))uploadProgresssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个带有构造体的POST请求。
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headersconstructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))blockprogress:(nullable void (^)(NSProgress *uploadProgress))uploadProgresssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个PUT请求。
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headerssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个PATCH请求。
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headerssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个DELETE请求。
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headerssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;// 发送一个通用的HTTP请求。
- (nullable NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)methodURLString:(NSString *)URLStringparameters:(nullable id)parametersheaders:(nullable NSDictionary <NSString *, NSString *> *)headersuploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressdownloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgresssuccess:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))successfailure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
五、使用示例
我们用 AFNetworking 来请求一张图片的数据,并展示到视图上:
- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.// 初始化并配置 imgViewself.imgView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 200, 200)];self.imgView.backgroundColor = [UIColor lightGrayColor];[self.view addSubview:self.imgView];[self downloadImgAndSave];
}// 下载图片并且保存
- (void)downloadImgAndSave {AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];manager.responseSerializer = [AFHTTPResponseSerializer serializer]; //设置响应序列化器:manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"image/jpeg", nil];__weak __typeof__(self) weakSelf = self;NSString *url = @"http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg";// 开始下载[manager GET:url parameters:nil headers:nil progress:^(NSProgress *downloadProgress) {NSLog(@"progress:%lld",downloadProgress.completedUnitCount);} success:^(NSURLSessionDataTask *task, id responseObject) {NSLog(@"图片下载完成");__strong __typeof__(weakSelf) strongSelf = weakSelf;strongSelf.imgView.image = [UIImage imageWithData:(NSData *)responseObject];} failure:^(NSURLSessionDataTask *task, NSError *error) {if (error) {NSLog(@"%@",error.userInfo);}}];
}@end
运行的结果如下:
总而言之,AFNetworking 实际上只是对 NSURLSession 高度地封装, 提供一些简单易用的 API 方便我们在 iOS 开发中发出网络请求并在其上更快地构建网络层组件并提供合理的接口。我们这个实例也证明了它使用的简便性。