NSURLProtocol无法截获NSURLSession解决方案
前言
在做知乎日报的项目时,在其设置界面中有一个选项,当开启时,可以在移动网络状况下不下载图片。这个需求的解决方案有两种,一种是在每一个使用网络图片的地方,在下载前进行判断,如果当前网络为移动蜂窝网络,且开启了在移动网络状况下不下载图片的选项时,放弃图片的下载;还有一种方案就是直接在NSURLProtocol中进行截获,在+ (BOOL)canInitWithRequest:(NSURLRequest *)request中进行判断,当request.URL请求为图片,并开启不下载图片选项,且当前为移动网络状况下,截获该请求。在知乎日报项目中,我使用的第二种,既子类NSURLProtocol来实现请求拦截。
NSURLProtocol实现图片下载拦截
一开始,觉得思路很简单,直接创建PictureBlockURLProtocol类,其是NSURLProtocol的子类,在Implement实现中定义如下:
1 | //获取当前网络状态 |
定义完PictureBlockURLProtocol类后,在AppDelegate类的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中调用[NSURLProtocol registerClass:[PictureBlockURLProtocol class]]方法进行注册,运行一下,看看效果,发现,当使用AFNetwork、SDWebImageCache等第三方库的图片请求并没有被截获到,而UIWebView可以,这就奇怪了,不过一想,UIWebView使用的是NSURLConnection,而AFNetWork、SDWebCache等已经废弃使用NSURLConnection,转而使用NSURLSession,难道是由于这个问题造成的?
在查看NSURLSession相关类的文档时(如下图),从文档可以看到,如果需要支持自定义的NSURLProtocol,需要将自定义的NSURLProtocol子类赋给NSURLSessionConfiguration的protocolClasses属性。所以,如果需要NSURLProtocol来截获NSURLSession发出的请求,需要每一个NSURLSession在创建时配置的NSURLSessionConfiguration类的protocolClasses属性附上自定义的NSURLProtocol。

根据上图的文档,注意到,对于后台Sessions,是不支持自定义的NSURLProtocol的。
通过查看AFNetwork、SDWebImageCache的源代码,其创建的Session时关联的NSURLSessionConfiguration为defaultSessionConfiguration。这样,解决方案就有两种,一种是直接改AFNetwork、SDWebImageCache等库的源代码,在其创建的NSURLSessionConfiguration实例中将自定义的NSURLProtocol赋给其protocolClasses属性;另一种方案,使用Method Swizzling方法,由于AFNetwork、SDWebImageCache在创建时使用的是[NSURLSessionConfiguration defaultSessionConfiguration],所以可以替换defaultSessionConfiguration实现。首先,创建NSURLSessionConfiguration的类别,代码如下:
1 | + (NSURLSessionConfiguration *)zw_defaultSessionConfiguration{ |
如上代码所示,在创建的每一个NSURLSessionConfiguration实例中,添加自定义的NSURLProtocol类到其protocolClasses属性,运行后,发现问题解决了,可以截获NSURLSession发出的请求了。
Done!!!!