第三方库(十二)

151 阅读7分钟

本文主要内容

一.AFNetworking
二.SDWebImageView
三.Reactive Cocoa
四.AsyncDisplayKit

截屏2022-09-28 14.42.50.png

一.AFNetworking

iOS客户端的第三方网络框架

1.1、框架图

AFNetworking这个第三方网络库中,首先由会话(即NSURLSession)部分组成;其次是网络监听模块,用来监听网络的变化,并且进行相关的逻辑处理;第三部分为网络安全模块。同时AFNetworking对请求和响应进行了序列化的封装,在此之上又有关于UIKit集成模块,如UIKit原生控件分类的添加,以上所有构成了AFNetworking整体框架

截屏2022-09-28 14.47.03.png

1.2、主要类关系图

AFNetworking库中核心类为AFURLSessionManager,核心类内部又包含会话NSURLSessionAFSecurityPolicy(进行网络证书的校验、公钥的校验等,保证网络安全)、AFNetworkingReachabilityManager(对网络连接进行监听)等内容。核心类有一个子类AFHTTPSessionManager,该子类包含AFURLRequestSer ialization(用来负责根据传进来的参数组装/拼接最终的结果,即NSMutableU RLRequest)和AFURLResponseSerialization(负责响应序列化,对网络请求返回结果进行解析,有对应解析方法,如json、image等的)。

总结来说,AFURLSessionManager主要工作

  • 创建和管理NSURLSession、NSURLSessionTask(对应一个网络请求);
  • 实现NSURLSessionDelegate等协议的代理方法;
  • 引入AFSecurityPolicy保证请求安全;
  • 引入AFNetworkingReachabilityManager监控网络状态。

截屏2022-09-28 14.49.55.png

例:发送一个GET方式的HTTP请求关键代码

// "AFHTTPSessionManager.m"

#pragma mark - 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 * _Nonnull, id _Nullable))failure {
    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                          headers:headers
                                                   uploadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];
    [dataTask resume];
    
    return dataTask;
}

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

    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];
    }

    if (serializationError) {
        if (failure) {
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }

        return nil;
    }

    __block NSURLSessionDataTask *dataTask = nil;
    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;
}

复制代码
// "AFURLRequestSerialization.m"

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error {

    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;

    for (NSString *keyPath in self.mutableObservedChangedKeyPaths) {
        [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
    }
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}
复制代码
// "AFURLSessionManager.m"
- (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 {

    NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];

    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}
复制代码

二.SDWebImage

一个异步下载图片并且支持缓存的框架

2.1、架构简图

SDWebImage框架中主要封装了一些UIKit的分类方法。其中SDWebImageManage r为其核心工作类,SDImageCache负责处理图片的缓存(包括磁盘缓存和内存缓存管理),SDWebImageDownloader负责具体图片的下载器。 截屏2022-10-29 14.53.25.png

2.2、加载图片的流程

详细流程描述

1、加载图片的入口方法为setImageWithURL:placeholderImage:option s:,先显示默认占位图placeholderImage
2、然后SDWebImageManager根据URL开始处理图片,调用方法downlo adWithURL:delegate:options:userInfo:,通过SDImageCache从缓存查找图片是否已经下载queryDiskCacheForKey:delegate:userInfo:; 3、先从内存图片缓存查找是否有图片,如果内存汇总已经有图片缓存, SDImageCacheDelegate回调imageCache:didFindImage:forKey:userI nfo:SDWebImageManagerSDWebImageManagerDelegate回调webI mageManager:didiFinishWithImage:UIImageView+WebCache等前端展示图片;
4、如果内存中没有,生成NSInvocationOperation添加到队列,开始从磁盘查找图片是否已经缓存;
5、根据URLKey在磁盘缓存目录下尝试读取图片文件,这一步是在NSOperation进行的操作,所以回主线程进行结果回调notifyDelegate:。如果从磁盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存),SDImageCacheDelegate回调imageCache:didFindImage:forKey:userInfo:,进而回调展示图片; 6、如果从磁盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调imageCache:didNotFindImageForKey:userIn fo:
7、共享或重新生成一个下载器SDWebImageDownloader开始下载图片,图片下载由NSURLConnection来做,实现相关delegate来判断图片下载中、下载完成和下载失败。connection:didReceiveData:中利用ImageIO做了按图片下载进度加载效果。connectionDidFinishLoading:数据下载完成后交给SDWebImageDecoder做图片解码处理。图片解码处理在一个NSOperationQueue完成,不会拖慢主线程UI,如有需要对下载的图片进行二次处理,最好也在这里完成,效率会高很多;
8、在主线程notifyDelegateOnMainThreadWithInfo:宣告解码完成, imageDecoder:didFinishDecodingImage:userInfo:回调给SDWebIma geDownloaderimageDownloader:didFinishWithImage:回调给SDW ebImageManager告知图片下载完成,通知所有的downloadDelegates下载完成,回调给需要的地方展示图片;
9、将图片保存到SDImageCache中,内存缓存和磁盘缓存同时保存,写文件到磁盘也单独在NSInvocationOperation中完成,避免拖慢主线程;

  • SDImageCache在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片;
  • SDWebImagePrefetcher可以预先下载图片,方便后续使用。

截屏2022-10-29 15.08.04.png

三.ReactiveCocoa

函数响应式编程框架

编程思想介绍

  • 函数式编程(Functional Programming):
    1、把操作尽量写成一系列嵌套的函数或者方法调用;
    2、每个方法必须有返回值(本身对象),把函数或者Block当作参数,block参数(需要操作的值)block返回值(操作结果),即每一步都需要有结果。
  • 响应式编程(Reactive Programming): 1、不需要考虑调用顺序,只需要知道考虑结果,即一个改变就会使结果改; 2、典型例子(AutoLayout):aView上添加子view,当aView约束发生变化时,子view也会随之改变。

如何导入Reactive Cocoa框架
通常使用CocoaPods导入。

  • ReactiveObjC -- 对应的是RAC的OC版本
  • Reactive Cocoa -- 对应的是RAC的swift版本

image.png

3.1、什么是函数响应式编程

ReactiveCocoa是一个函数响应式编程框架,可以订阅一个信号就是函数响应式编程的核心概念。 截屏2022-10-29 15.56.04.png

3.2、信号

  • 该框架中的核心类为RACSignal

截屏2022-10-29 15.58.40.png

  • 通过父类RACStream的组成结构,了解信号 截屏2022-10-29 16.01.36.png

  • 信号代表一连串的状态
    如下图,信号处于1状态,由于数据的变化,会从1状态切换到2状态,数据再变化会导致信号继续变化,最后是end

      在状态改变时,对应的订阅者RACSubscriber就会收到通知执行响应的指令。
    复制代码
    

截屏2022-10-29 16.05.55.png

  • RACSignal包含子类RACReturnSignalRACDynamicSignal。以下通过看源码理解信号

RACDynamicSignal

// RACReturnSignal.m
+ (RACSignal *)return:(id)value {
    ......
    RACReturnSignal *signal = [[self alloc] init];
    signal->_value = value;
    ......
}
复制代码

RACDynamicSignal

// RACSignal.m
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    return [RACDynamicSignal createSignal: didSubscribe];
}

// RACDynamicSignal.m
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
    RACDynamicSignal *signal = [[self alloc] init];
    signal->_didSubscribe = [didSubscribe copy];
    return [signal setNameWithFormat:@"+createSignal:"];
}


复制代码

3.3、订阅

  • 订阅者RACSubscriber

3.3.1 、订阅的逻辑流程

首先开始订阅一个信号RACSignal,然后调用RACSignal-subscriveNext:方法,该方法中会产生一个RACSubscriber类,调用RACSignal-sendNext:方法,在-sendNext:方法内部又会调用-sendCompleted方法,结束订阅流程。

截屏2022-11-05 11.49.30.png

3.3.2、RACSubscriber订阅的内部原理

当产生一个RACSubscriber类时,会在其内部持有一个成员变量,即didSubscribe(Block),调用RACSubscriber时会调用此Block,如下:

{
    [RACSignal return:@3];
    
    [RACSignal subScribedNext:^(id x){
        NSLog(@"%@",x);
    }];
}
复制代码

截屏2022-11-05 14.12.29.png

四.AsyncDisplayKit

提升iOS界面渲染性能的一个框架

4.1、主要处理问题

  • 解决布局的耗时运算
  • 解决渲染问题
  • 处理UIKit对象 将可以移到子线程中处理的问题移到子线程处理。 截屏2022-11-05 14.38.06.png

4.2、基本原理

  • 1、针对ASNode的修改和提交,会对其进行封装并提交到一个全局容器当中;
  • 2、ASDK也在Runloop中注册一个Observer;
  • 3、当Runloop进入休眠前,ASDK执行该loop内提交的所有任务。

截屏2022-11-05 14.42.33.png

本文总结

AFNetworking的整体结构是怎样的?⭐️⭐️⭐️

SDWebImage框架是怎样加载图片的?⭐️⭐️⭐️

RAC的信号订阅是什么意思?

ASDK的实现原理是怎样的?⭐️⭐️⭐️