AFNetworking源码:AFNetworking中的那些巧妙设计

简介:

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


我们都知道AFNetworking是一个非常好用且常见的网络库,那么AFNetworking的开发者是如何做到的呢?AFNetworking中有哪些巧妙设计是我们还不知道,以后开发中可以借鉴的呢?

一、利用runtime黑魔法

1.方法交换(swizzle)

目的:

   这里方法替换的目的主要是想在调用系统的NSURLSessionTask 的resume方法时,能够发送AFNSURLSessionTaskDidResumeNotification通知,以达到监测系统方法调用的目的。

实现:

   _AFURLSessionTaskSwizzling类在+load方法中将_AFURLSessionTaskSwizzling 中的af_resume方法与NSURLSessionTask的resume方法交换。

1

@end

@implementation _AFURLSessionTaskSwizzling

  • (void)load { if (NSClassFromString(@"NSURLSessionTask")) { NSURLSessionConfiguration configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; NSURLSession session = [NSURLSession sessionWithConfiguration:configuration];

2

3

4

  • (void)af_resume { NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_resume];

if (state != NSURLSessionTaskStateRunning) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; } }

疑问

   通常我们需要实现这种操作的方式是实现一个子类,然后使用的时候使用子类。但是AFNetworking并不想改变我们使用NSURLSessionTask的方式,所以采用了这种巧妙的方式。

到这里大部分人可能会有以下三个疑问,理解了这几个疑问也就理解了为什么说这里设计很巧妙。

  • a .为什么要在+load中实现交换? 因为load方法的实现肯定是在方法调用之前,在这里实现交换可以确保调用在交换之后发生。 load方法里实现还有一个好处,那就这个方法由系统自动调用,不用去在乎调用时机和由谁发起调用。_AFURLSessionTaskSwizzling是一个内嵌类,也就是说这个类只在.m中定义和实现不需要暴露给用户,这个类的唯一作用就是替换方法,也不需要被实例化或者被别的实例引用。
  • b.af_resume的实现里又调用了[self af_resume],不会造成死循环吗? 解释这个问题很简单,因为知道方法交换的原理就不难理解了。 替换之前:NSURLSessionTask的resume.png

替换之后:resume的调用.png可以看到

1 . 给resume发送消息的时候,实际是调用af_resume的实现。    

2 .在af_resume中给af_resume 发送消息,实际是调用resume的实现。

  • c.为什么想替换NSURLSessionTask的resume方法没有直接使用NSURLSessionTask类,而是通过遍历localDataTask的父类逐级替换? 这个疑问其实在代码注释中已经给出了解释:

5

6

大意是:

  1. 在OC的实现中,NSURLSessionTask的类并不是NSURLSessionTask而是依靠类族. 也就是[NSURLSessionTask class]返回的结果并不是我们想要的结果,NSCFURLSessionTask才是实际的类。    
  2. iOS8中的resmue是唯一的实现,而iOS7中NSCFLocalSessionTask并没有调用super的resume,NSCFURLSessionTask和NSCFLocalSessionTask都实现了resume,所以需要循环调用superclass把两个实现都替换掉。 所以开发者采用了这种方式确保所有版本的所有resume方法都会被替换掉。

2.关联变量

目的

在UIImageView的分类中的类方法中,给UIImageView的类添加关联变量。

实现

7

  • (AFImageDownloader *)sharedImageDownloader { return objc_getAssociatedObject([UIImageView class], @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance]; }
  • (void)setSharedImageDownloader:(AFImageDownloader *)imageDownloader { objc_setAssociatedObject([UIImageView class], @selector(sharedImageDownloader), imageDownloader, OBJC_ASSOCIATION_RETAIN_NONATOMIC); }
疑问

同样的理解了一下几个疑问的原因,也就知道了设计的巧妙之处。

a .为什么要用分类和关联变量? 不用改变UIImageView的类,也不用继承。

b .为什么要在UIImageView类中添加关联变量? 因为这个imageDownloader是属于所有UIImageView的,并不属于某一个UIImageView的实例。也就是说所有UIImageView的实例都是使用这个imageDownloader去请求图片。所以把imageDownloader与UIImageView的类关联是合理的。

c .这么做的好处? 调用imageDownloader有类似单例的便捷:

(void)testResponseIsNilWhenLoadedFromCache { AFImageDownloader *downloader = [UIImageView sharedImageDownloader]; ... }

其实这里设计的巧妙之处不仅是这些,关联变量的key使用@selector(sharedImageDownloader)也是一个很巧妙的应用,因为这样就不需要单独去声明一个key,而且利用了属性本身的名称,即简单又明了。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/zhibo

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-04-09
本文作者:jlstmac
本文来自:“cocoachina”,了解相关信息可以关注“cocoachina

相关文章
|
6月前
|
Swift iOS开发
Alamofire和AFNetworking有什么区别?
Alamofire和AFNetworking有什么区别?
174 4
|
6月前
|
Swift iOS开发 Perl
如何在项目中使用Alamofire和AFNetworking?
如何在项目中使用Alamofire和AFNetworking?
78 3
|
JSON 前端开发 数据可视化
umi3源码探究简析
作为蚂蚁金服整个生态圈最为核心的部分,umi可谓是王冠上的红宝石,因而个人认为对于整个umi架构内核的学习及设计哲学的理解,可能比如何使用要来的更为重要;作为一个使用者,希望能从各位大佬的源码中汲取一些养分以及获得一些灵感
231 0
|
SQL 程序员 数据库
Flutter(二十九)——封装SQLHelpers
居天下之广居,立天下之正位,行天下之大道;得志,与民由之;不得志,独行其道。富贵不能淫,贫贱不能移,威武不能屈,此之谓大丈夫。
372 2
Flutter(二十九)——封装SQLHelpers
|
存储 前端开发 JavaScript
werkzeug源码阅读-完结篇
Werkzeug是一个全面的WSGI Web应用程序库。它最初是WSGI实用程序各种工具的简单集合,现已成为最高级的WSGI实用程序库之一,是Flask背后的项目。
503 0
werkzeug源码阅读-完结篇
|
存储 缓存 C++
iOS-底层原理 15:dyld加载流程
iOS-底层原理 15:dyld加载流程
384 0
iOS-底层原理 15:dyld加载流程
|
iOS开发 MacOS
iOS-底层原理 01:源码探索的三种方式
iOS-底层原理 01:源码探索的三种方式
175 0
iOS-底层原理 01:源码探索的三种方式
|
程序员 开发工具 iOS开发
iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试(三)
作为一名iOS程序员,探索OC底层原理永不止息,同时也是永远的痛,最开始只能靠猜测!后面慢慢找到了苹果官方开源的源码来辅助看一下,但是尽管这样,还是显得不太直观!如果objc源码能够像我们自己创建的项目一样直接编译调试,像我们自己的代码一样能够直接 LLDB 调试,流程跟踪,那简直不要太爽。废话不多说,开炮~ 哦,不是,是开干~!🚀
iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试(三)
|
Unix 程序员 iOS开发
iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试(二)
作为一名iOS程序员,探索OC底层原理永不止息,同时也是永远的痛,最开始只能靠猜测!后面慢慢找到了苹果官方开源的源码来辅助看一下,但是尽管这样,还是显得不太直观!如果objc源码能够像我们自己创建的项目一样直接编译调试,像我们自己的代码一样能够直接 LLDB 调试,流程跟踪,那简直不要太爽。废话不多说,开炮~ 哦,不是,是开干~!🚀
iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试(二)
|
Unix 程序员 开发工具
iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试(一)
作为一名iOS程序员,探索OC底层原理永不止息,同时也是永远的痛,最开始只能靠猜测!后面慢慢找到了苹果官方开源的源码来辅助看一下,但是尽管这样,还是显得不太直观!如果objc源码能够像我们自己创建的项目一样直接编译调试,像我们自己的代码一样能够直接 LLDB 调试,流程跟踪,那简直不要太爽。废话不多说,开炮~ 哦,不是,是开干~!🚀
iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试(一)