博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
AFNetworking源码阅读1
阅读量:6998 次
发布时间:2019-06-27

本文共 14781 字,大约阅读时间需要 49 分钟。

#前言:AFNetworking是一款非常有名网络框架,笔者也是用了挺久了,但是也是仅限于使用一些方法。最近一直在思考,要想成为一名优秀的开发者,肯定不能仅仅停留在会用的阶段。所以痛定思痛,这次下定决心,自己的第一遍博客就从AFN开始。

从上图AFN的目录我们可以看出,ANF大概分为了5个类:

  1. NSURLSession(通信模块,对NSURLSession的封装,也是AFN最核心的地方)
  2. Reachability(网络状态监听模块)
  3. Security(网络安全模块)
  4. Serialization(数据解析模块)
  5. UIKIT(UI模块)

下面我会按着模块来分析:

#通信模块

##先从NSURLSession开始分析:

@interface AFHTTPSessionManager : AFURLSessionManager 
复制代码

打开AFHTTPSessionManager,首先就能明白AFHTTPSessionManager继承自AFURLSessionManager,属于这个通信模块的最上层。

+ (instancetype)manager {    return [[[self class] alloc] initWithBaseURL:nil];}- (instancetype)init {    return [self initWithBaseURL:nil];}- (instancetype)initWithBaseURL:(NSURL *)url {    return [self initWithBaseURL:url sessionConfiguration:nil];}- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {    return [self initWithBaseURL:nil sessionConfiguration:configuration];}复制代码

而在其.m中看看到初始化,[AFHTTPSessionManager manager]也就是我们日常调用AFN开始网络通信的初始化。其用的设计模式就是工厂模式。

*/- (instancetype)initWithBaseURL:(NSURL *)url           sessionConfiguration:(NSURLSessionConfiguration *)configuration{    //调用父类初始化方法    self = [super initWithSessionConfiguration:configuration];    if (!self) {        return nil;    }    /*     为了确保NSURL +URLWithString:relativeToURL: works可以正确执行,在baseurlpath的最后添加‘/’     */    //url有值且没有‘/’,那么在url的末尾添加‘/’    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {        url = [url URLByAppendingPathComponent:@""];    }    self.baseURL = url;    //给requestSerializer、responseSerializer设置默认值    self.requestSerializer = [AFHTTPRequestSerializer serializer];    self.responseSerializer = [AFJSONResponseSerializer serializer];    return self;}复制代码

其中self = [super initWithSessionConfiguration:configuration];会去调用父类(AFURLSessionManager)的initWithSessionConfiguration的初始化方法,进行一些url初始化的配置

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {    self = [super init];    if (!self) {        return nil;    }    //设置默认的configuration,配置我们的session    if (!configuration) {        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];    }    //持有configuration    self.sessionConfiguration = configuration;    //设置为delegate的操作队列并发的线程数量1,也就是串行队列    //避免了线程的不安全,    self.operationQueue = [[NSOperationQueue alloc] init];    self.operationQueue.maxConcurrentOperationCount = 1;    /*     -如果完成后需要做复杂(耗时)的处理,可以选择异步队列     -如果完成后直接更新UI,可以选择主队列     [NSOperationQueue mainQueue]     */        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];    //默认为json解析    self.responseSerializer = [AFJSONResponseSerializer serializer];    //设置默认证书 无条件信任证书https认证    self.securityPolicy = [AFSecurityPolicy defaultPolicy];#if !TARGET_OS_WATCH    //网络状态监听    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];#endif    //delegate= value taskid = key    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];    //使用NSLock确保线程安全    self.lock = [[NSLock alloc] init];    self.lock.name = AFURLSessionManagerLockName;        //异步的获取当前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;}复制代码

以上就是AFN在初始化的网络模块运行的方法 其核心全局的session也是在URLSessionManager中创建出来

AFHTTPSessionManager作为一个供开发者调用的模块,其.h暴露出来的API都是简单明了,只需要提供必要的URL和params就可以通过block返回结果。

下面我们先简单看下ANF在请求的过程中的流程

- (NSURLSessionDataTask *)GET:(NSString *)URLString                   parameters:(id)parameters                     progress:(void (^)(NSProgress * _Nonnull))downloadProgress                      success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success                      failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure{    //返回一个task,然后开始网络请求    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"                                                        URLString:URLString                                                       parameters:parameters                                                   uploadProgress:nil                                                 downloadProgress:downloadProgress                                                          success:success                                                          failure:failure];    //开始网络请求    [dataTask resume];    return dataTask;}- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *) method                                       URLString:(NSString *)URLString                                      parameters:(id)parameters                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress                                         success:(void (^)(NSURLSessionDataTask *, id))success                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure{    NSError *serializationError = nil;    /*     1.先调用AFHTTPRequestSerializer的requestWithMethod函数构建request     2.处理request构建产生的错误 – serializationError     //relativeToURL表示将URLString拼接到baseURL后面     */    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];    if (serializationError) {        if (failure) {            //http://fuckingclangwarnings.com/#semantic            //xcode忽略编译器的警告,diagnostic:诊断的#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wgnu"            //completionQueue不存在返回dispatch_get_main_queue            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{                failure(nil, serializationError);            });#pragma clang diagnostic pop        }        return nil;    }    //此时的request已经将参数拼接在url后面    __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;}复制代码

不管是调用GET/POST(除了表单,但是表单是调用一个返回NSURLSessionDataTask的方法)/DELETE/PATCH/PUT,都会调用dataTaskWithHTTPMethod

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];复制代码

在上面的方法中,生成了一个request对象

//使用session和request来创建一个NSURLSessionDataTask对象 dataTask = [self.session dataTaskWithRequest:request]; [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];复制代码

通过session和request来生成一个task

AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];    delegate.manager = self;    delegate.completionHandler = completionHandler;    [self setDelegate:delegate forTask:dataTask];复制代码

而task和delegate绑定在一起

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate            forTask:(NSURLSessionTask *)task{    //task和delegate都不能为空    NSParameterAssert(task);    NSParameterAssert(delegate);    //加锁确保中间代码块是原子操作,线程安全    [self.lock lock];    //将delegate存入字典,以taskid作为key,说明每个task都有各自的代理    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;    //设置两个NSProgress的变量 - uploadProgress和downloadProgress,给session task添加了两个KVO事件    [delegate setupProgressForTask:task];    [self addNotificationObserverForTask:task];    [self.lock unlock];}- (void)setupProgressForTask:(NSURLSessionTask *)task {        ...            //给task和progress添加kvo    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))              options:NSKeyValueObservingOptionNew              context:NULL];    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))              options:NSKeyValueObservingOptionNew              context:NULL];    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))              options:NSKeyValueObservingOptionNew              context:NULL];    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))              options:NSKeyValueObservingOptionNew              context:NULL];        ...}//在KVO回调方法把结果回调出去- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary
*)change context:(void *)context { ... if (self.downloadProgressBlock) { self.downloadProgressBlock(object); } ... if (self.uploadProgressBlock) { self.uploadProgressBlock(object); } ...}复制代码

从以上可以看出:session是唯一的 request和task和delegate是一一对应的,delegate承担着数据处理并转发的功能。

该模块中的一些重点的功能:

//-如果完成后需要做复杂(耗时)的处理,可以选择异步队列//-如果完成后直接更新UI,可以选择主队列// [NSOperationQueue mainQueue] self.operationQueue = [[NSOperationQueue alloc] init]; self.operationQueue.maxConcurrentOperationCount = 1; self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];复制代码

delegate在哪个OperationQueue回调,如果我们将其设置为[NSOperationQueue mainQueue]就能在主线程进行回调非常的方便。但是不能把耗时操作放在主线程,所以创建了一个异步的串行队列。

//delegate= value taskid = key    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];        //使用NSLock确保mutableTaskDelegatesKeyedByTaskIdentifier线程安全    self.lock = [[NSLock alloc] init];    self.lock.name = AFURLSessionManagerLockName;        //再字典的set get remove中加锁,确保线程安全                复制代码

思考?: NSlock可以不可以换成信号量,信号量的效率会高一点

#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);#define UNLOCK(lock) dispatch_semaphore_signal(lock);@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock;.....- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {    ...        LOCK(self.weakCacheLock);        [self.weakCache setObject:obj forKey:[[key mutableCopy] copy]];        UNLOCK(self.weakCacheLock);    ...}- (void)removeObjectForKey:(id)key {    ...        // Remove weak cache        LOCK(self.weakCacheLock);        [self.weakCache removeObjectForKey:key];        UNLOCK(self.weakCacheLock);}- (id)objectForKey:(id)key {    ...        LOCK(self.weakCacheLock);        obj = [self.weakCache objectForKey:key];        UNLOCK(self.weakCacheLock);    ...}复制代码

而另一款优秀的图片库SDWebImage中用信号量保证线程的安全

[task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))              options:NSKeyValueObservingOptionNew              context:NULL];    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))              options:NSKeyValueObservingOptionNew              context:NULL];    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))              options:NSKeyValueObservingOptionNew              context:NULL];    [task addObserver:self           forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))              options:NSKeyValueObservingOptionNew              context:NULL];复制代码

反射机制(对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为反射机制。) 在delegate的setupProgressForTask中运用反射机制给task添加KVO

@implementation _AFURLSessionTaskSwizzling+ (void)load {        // 1) 首先构建一个NSURLSession对象session,再通过session构建出一个_NSCFLocalDataTask变量        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wnonnull"        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];#pragma clang diagnostic pop        // 2) 获取到af_resume实现的指针        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));        Class currentClass = [localDataTask class];        // 3) 检查当前class是否实现了resume。如果实现了,继续第4步        while (class_getInstanceMethod(currentClass, @selector(resume))) {            // 4) 获取到当前class的父类(superClass)            Class superClass = [currentClass superclass];            // 5) 获取到当前class对于resume实现的指针            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));           //  6) 获取到父类对于resume实现的指针            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));           // 7) 如果当前class对于resume的实现和父类不一样(类似iOS7上的情况),并且当前class的resume实现和af_resume不一样,才进行method swizzling。            if (classResumeIMP != superclassResumeIMP &&                originalAFResumeIMP != classResumeIMP) {                [self swizzleResumeAndSuspendMethodForClass:currentClass];            }            // 8) 设置当前操作的class为其父类class,重复步骤3~8            currentClass = [currentClass superclass];        }                [localDataTask cancel];        [session finishTasksAndInvalidate];    }}...- (void)af_resume {    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");    NSURLSessionTaskState state = [self state];    [self af_resume];    // 因为经过method swizzling后,此处的af_resume其实就是之前的resume,所以此处调用af_resume就是调用系统的resume。但是在程序中我们还是得使用resume,因为其实际调用的是af_resume    // 如果之前是其他状态,就变回resume状态,此处会通知调用taskDidResume    if (state != NSURLSessionTaskStateRunning) {        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];    }}...@end复制代码

其实看类的名字就可以知道是使用methodswizzle这种黑魔法实现AOP模式 通过调用resume,发起网络请求,同时发送一个通知到sessionManamer,由它统一发送一个状态到UIKIT类里面,来改变视图的变化。

转载于:https://juejin.im/post/5ca8c41df265da30933fd70e

你可能感兴趣的文章
.NET深入解析LINQ框架(二:LINQ优雅的前奏)
查看>>
虚弱的原则
查看>>
我的友情链接
查看>>
为什么网络棋牌的分成那么高?
查看>>
答案永远在现场
查看>>
博客九周年:稳中求胜 持续发展
查看>>
高效软文营销写作技巧:产生共鸣方是王道
查看>>
一边卖不掉,一边买不到,我看电商的“危”与“机”
查看>>
was日志报检测到cpu饥饿
查看>>
Powershell管理系列(二十八)PowerShell操作之修改AD账号密码
查看>>
Sencha应用程序的UI测试
查看>>
Azure运维系列 1:使用Azure云助理掌控云中机房
查看>>
简单安装Oracle网格控制器agent端
查看>>
mysql的group_concat函数
查看>>
让“云”无处不在-Citrix Xenserver之三 license server
查看>>
10分钟被动添加20精准粉丝,有手机就能操作!
查看>>
活动分区丢失导致的Windows 8无法启动
查看>>
轻松上手移动互联——百度SiteApp建造日志
查看>>
注意你生活中的非政府“棱镜”组织
查看>>
Eclipse下Pydev在线安装失败及解决办法
查看>>