源码学习

AFNetworking

基于NSURLSession的封装。主要解决请求封装统一化,数据自动解析,多任务并发,线程调度和队列管理,网络状态监听。

AFURLSessionManager

对NSURLSession的封装。统一管理请求,回调,进度,响应序列化。

设计模式:

使用了模板方法模式,定义了统一的网络请求流程骨架,通过代理方法和block回调让子类或外部自定义细节。比如请求完成后的解析,进度回调等。

如何管理task和回调
  1. 根据NSURLRequset创建NSURLSessionDataTask
  2. 根据task创建AFURLSessionManagerTaskDelegate。delegate里包含这个task的progress,completionHandler以及responseSerializer
  3. 以taskID为key,delegate为value,存入字典里保存映射。
  4. 在收到urlsession task回调时,根据task找到delegate,交给AFURLSessionManagerTaskDelegate处理
  5. 在AFURLSessionManagerTaskDelegate中转为block,主线程返回。
线程安全

AFNetworking对mutableTaskDelegatesKeyedByTaskIdentifier的读写都用了nslock保证安全。

 dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });

这里将所有成功失败收集到group中,后续可以通过group_notify统一收尾

- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
    __block NSArray *tasks = nil;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
            tasks = dataTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
            tasks = uploadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
            tasks = downloadTasks;
        } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
        }

        dispatch_semaphore_signal(semaphore);
    }];

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    return tasks;
}

这里用了dispatch_semaphore加锁保证线程安全。

AFHTTPSessionManager

AFURLSessionManager的子类,针对GET,POST请求进一步封装。

AFURLRequestSerialiazation(response)

负责请求和响应的数据解析
设置好数据类型,比如JSON,XML,Image,请求完成后自动解析。
设计模式:
责任链模式。
依次遍历JSON,XML,Image,谁能处理水处理。

AFNetworkingReachabilityManager

实现网络状态变化监听。
设计模式:
单例模式。

AFImageDownloader

image+afnetworking里动态添加了AFImageDownloader(可以替换其他downloader)
image+afnetworking管理逻辑,最后调用AFImageDownloader下载
储存了当前的图片下载凭证,可以取消当前下载,
单次下载:

  1. 根据urlid判断此task是否已经存在
  2. 缓存相关
    3.构建sessionmanager,进行下载
    4.将回调handler绑定在mergedtask上
    5.成功后遍历所有handler给回调
    多个下载:
    mergedtask小于最大并发数时直接开始并发下载,默认4。mergedtask.task resume
    大于最大并发数进去queuedMergedTasks列表中

AFAutoPurgingImageCache

AFCachedImage

图片类。主要包含图片,唯一标识,容积,最近修改时间等
重写了get方法,每次获取iamge的时候都会更新当前时间作为最新访问时间。
提供两个协议:
1 图片处理协议:增删查 @protocol AFImageCache
2 网络协议:request相关 @protocol AFImageRequestCache

AFAutoPurgingImageCache: <AFImageRequestCache>

任务:数据校验,加入图片,ID管理,线程安全,容积管理。
缓存属性:缓存总容量,当前使用容量,用户偏好的清除后剩余容量。
初始化时指定了缓存总容量100,清理后的剩余容量60
加入图片:
加入图片使用dispatch_barrier_async+并发队列保证线程安全。
根据id获取之前缓存图片,如果之前有缓存,去掉缓存更新容积
存入新图片
在addimage的时候自动清理工具:
自动清理缓存方法使用了dispatch_barrier_async+并发队列保证线程安全。
1 总容量-清理后留存容量=目标清理容量。
2 缓存数据按照最后访问日期排序。
3 使用id删除图片
4 删除的图片记录容积,确定当前容积

常见问题

1. HTTPS双向认证

AFNetworking通过AFSecurityPolicy管理HTTPS的服务端证书校验,同时再AFURLSessionManager中通过实现didReceiveChallenge方法,支持HTTPS双向认证,在收到服务器的challenge时,读取本地p12证书,返回完成验证。

2. runloop

AFNetworking2.0基于NSURLConnection的封装会使用runloop进行子线程保活。但是AFNetworing3.0开始,基于NSURLSession的封装,其自身处理了runloop,所以不需要额外处理。

3. 线程安全

AFNetworking对mutableTaskDelegatesKeyedByTaskIdentifier的读写都用了nslock保证安全。
dispatch_semaphore_t 保证对self.session的task读取安全。
回调在主线程
AFAutoPurgingImageCache使用dispatch_barrier_async+并发队列保证线程安全.

// 读操作(并发)
- (UIImage *)imageForRequest:(NSURLRequest *)request {
    __block UIImage *image;
    dispatch_sync(self.synchronizationQueue, ^{
        image = self.cachedImages[request.URL.absoluteString];
    });
    return image;
}

// 写操作(屏障)
- (void)addImage:(UIImage *)image forRequest:(NSURLRequest *)request {
    dispatch_barrier_async(self.synchronizationQueue, ^{
        self.cachedImages[request.URL.absoluteString] = image;
    });
}

dispatch_sync的使用是由于需要立刻返回值。看起来时串行。但如果在不同线程里调用imageForRequest,实际上是并发的。dispatch_barrier_async可以保证写安全。

4. 缓存机制

AFNetworking 的缓存机制本质是对 NSURLCache 的封装,适合标准 HTTP 缓存场景。对于图片等特定资源,它提供了额外的内存缓存优化

5. 请求和响应序列化

AFJSONRequestSerializer
将参数序列化为 JSON 格式的请求体(Content-Type: application/json)
AFCompoundResponseSerializer
组合多个序列化器,按顺序尝试解析:AFJSONResponseSerializer,AFXMLParserResponseSerializer,AFImageResponseSerializer

6. 如何用 AFNetworking 实现文件上传和下载?

提供直接上传和分块上传(进度回调)。
支持断点续传

7.如何优化 AFNetworking 的请求性能?

AFHTTPSessionManager 封装后使用单例模式
进行配置:连接数,超时时间,设置缓存等
将耗时的数据解析放在子线程,比如sdwebimage的图片解码在子线程

8. AFNetworking 如何处理大量并发请求?

有最大的并发数限制,可以设置更改。有队列进行调度。可以设置优先级。

9.AFNetworking 和 NSURLSession 直接使用有什么区别?

内置序列化。
安全性
线程安全,自动回主线程
网络状态检测
错误封装,请求进度

YTKNetwok

在AFNetworking的基础上二次封装。
每个 API 封装为独立类,参数集中管理
自带链式调用和批量请求
支持requestInterceptor 和 responseInterceptor提供了统一的预处理和后处理能力,统一签名,日志,重试等逻辑。

SDWebimage

主流程分解

  • URL 校验(sd_internalSetImageWithURL: 方法)。
  • 内存缓存查询(SDImageCache 的 queryCacheOperationForKey:)。
  • 磁盘缓存查询(通过 NSURLCache 或自定义磁盘路径)。
  • 网络下载(SDWebImageDownloader 发起异步请求,支持并发队列、进度回调)。
  • 图片解码(在后台线程通过 CGContextDrawImage 避免主线程卡顿)。
  • 缓存写入(内存缓存 NSCache + 磁盘缓存异步存储)。
1. 如何避免重复下载

缓存机制。
下载去重:先判断是否已经有相同的请求了,如果有相同的请求就复用,把回调加到回调组里。如果没有再新建。

2. 黑名单机制

当一个url无效或者无法解析时,加入到黑名单里,下次直接返回失败,避免资源浪费。会在App重启时清除,也可以手动清除。

3. 与cell复用问题

cell会复用,快速滚动时会多次切换图片url,图片下载异步,容易出现图片没下载好,已经复用的情况。造成图片错位。
sdwebiamge的解决方案:统一imageview发起新的会把之前的取消,防止错误覆盖。
我们能做的优化: 设置sdwebiamge加载图片为低优先级,滚动时延迟加载。
或许可以自定义缓存路径,避免与其他缓存共用,提升查询命中率。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容