美文网首页 ios零碎记录暂时没看却需要看的第三方框架
AFNetworking 3.0 源码解读 总结(干货)(下)

AFNetworking 3.0 源码解读 总结(干货)(下)

作者: 老马的春天 | 来源:发表于2016-09-02 15:50 被阅读965次

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上)

    21.网络服务类型NSURLRequestNetworkServiceType

    示例代码:

    typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)
    {
        NSURLNetworkServiceTypeDefault = 0,    // Standard internet traffic
        NSURLNetworkServiceTypeVoIP = 1,    // Voice over IP control traffic
        NSURLNetworkServiceTypeVideo = 2,    // Video traffic
        NSURLNetworkServiceTypeBackground = 3, // Background traffic
        NSURLNetworkServiceTypeVoice = 4       // Voice data
    };
    

    可以通过这个值来指定当前的网络类型,系统会跟据制定的网络类型对很多方面进行优化,这个就设计到很细微的编程技巧了,可作为一个优化的点备用。

    22.Authorization字段

    在请求头中可以添加Authorization字段。

    Authorization: Basic YWRtaW46YWRtaW4= 其中Basic表示基础认证,当然还有其他认证,如果感兴趣,可以看看本文开始提出的那本书。后边的YWRtaW46YWRtaW4= 是根据username:password 拼接后然后在经过Base64编码后的结果。

    如果header中有 Authorization这个字段,那么服务器会验证用户名和密码,如果不正确的话会返回401错误。

    23.使用流写数据

    下边的代码可以说是使用流写数据的经典案例。可直接拿来使用。

    示例代码:

    - (NSMutableURLRequest *)requestWithMultipartFormRequest:(NSURLRequest *)request
                                 writingStreamContentsToFile:(NSURL *)fileURL
                                           completionHandler:(void (^)(NSError *error))handler
    {
        NSParameterAssert(request.HTTPBodyStream);
        NSParameterAssert([fileURL isFileURL]);
    
        // 加上上边的两个判断,下边的这些代码就是把文件写到另一个地方的典型使用方法了
        NSInputStream *inputStream = request.HTTPBodyStream;
        NSOutputStream *outputStream = [[NSOutputStream alloc] initWithURL:fileURL append:NO];
        __block NSError *error = nil;
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
            [inputStream open];
            [outputStream open];
    
            // 读取数据
            while ([inputStream hasBytesAvailable] && [outputStream hasSpaceAvailable]) {
                uint8_t buffer[1024];
    
                NSInteger bytesRead = [inputStream read:buffer maxLength:1024];
                if (inputStream.streamError || bytesRead < 0) {
                    error = inputStream.streamError;
                    break;
                }
    
                NSInteger bytesWritten = [outputStream write:buffer maxLength:(NSUInteger)bytesRead];
                if (outputStream.streamError || bytesWritten < 0) {
                    error = outputStream.streamError;
                    break;
                }
    
                if (bytesRead == 0 && bytesWritten == 0) {
                    break;
                }
            }
    
            [outputStream close];
            [inputStream close];
    
            if (handler) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    handler(error);
                });
            }
        });
    
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
        mutableRequest.HTTPBodyStream = nil;
    
        return mutableRequest;
    }
    

    24.NSIndexSet

    定义:NSIndexSet是一个有序的,唯一的,无符号整数的集合。

    我们先看个例子:

    NSMutableIndexSet *indexSetM = [NSMutableIndexSet indexSet];
        [indexSetM addIndex:19];
        [indexSetM addIndex:4];
        [indexSetM addIndex:6];
        [indexSetM addIndex:8];
        [indexSetM addIndexesInRange:NSMakeRange(20, 10)];
        
        [indexSetM enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
            NSLog(@"%lu",idx);
        }];
    

    打印结果如下:

    2016-08-10 11:39:00.826 xxxx[3765:100078] 4
    2016-08-10 11:39:00.827 xxxx[3765:100078] 6
    2016-08-10 11:39:00.827 xxxx[3765:100078] 8
    2016-08-10 11:39:00.827 xxxx[3765:100078] 19
    2016-08-10 11:39:00.827 xxxx[3765:100078] 20
    2016-08-10 11:39:00.828 xxxx[3765:100078] 21
    2016-08-10 11:39:00.828 xxxx[3765:100078] 22
    2016-08-10 11:39:00.828 xxxx[3765:100078] 23
    2016-08-10 11:39:00.828 xxxx[3765:100078] 24
    2016-08-10 11:39:00.828 xxxx[3765:100078] 25
    2016-08-10 11:39:00.828 xxxx[3765:100078] 26
    2016-08-10 11:39:00.828 xxxx[3765:100078] 27
    2016-08-10 11:39:00.828 xxxx[3765:100078] 28
    2016-08-10 11:39:00.829 xxxx[3765:100078] 29
    

    这充分说明了一下几点

    • 它是一个无符号整数的集合
    • 用addIndex方法可以添加单个整数值,使用addIndexesInRange可以添加一个范围,比如NSMakeRange(20, 10) 取20包括20后边十个整数
    • 唯一性,集合内的整数不会重复出现
    • 具体用法就不再次做详细的解释了,有兴趣的朋友可以看看这篇文章: NSIndexSet 用法

    25.NSUnderlyingErrorKey优先错误

    当出现可能会有两个错误的情况下,可以考虑使用NSUnderlyingErrorKey处理这种情况。

    示例代码:

    static NSError * AFErrorWithUnderlyingError(NSError *error, NSError *underlyingError) {
        if (!error) {
            return underlyingError;
        }
    
        if (!underlyingError || error.userInfo[NSUnderlyingErrorKey]) {
            return error;
        }
    
        NSMutableDictionary *mutableUserInfo = [error.userInfo mutableCopy];
        mutableUserInfo[NSUnderlyingErrorKey] = underlyingError;
    
        return [[NSError alloc] initWithDomain:error.domain code:error.code userInfo:mutableUserInfo];
    }
    

    26.NSJSONReadingOptions

    这个选项可以设置json的读取选项,我们点进去可以看到:

    typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
        NSJSONReadingMutableContainers = (1UL << 0),
        NSJSONReadingMutableLeaves = (1UL << 1),
        NSJSONReadingAllowFragments = (1UL << 2)
    } NS_ENUM_AVAILABLE(10_7, 5_0);
    
    • NSJSONReadingMutableContainers 这个解析json成功后返回一个容器
    • NSJSONReadingMutableLeaves 返回中的json对象中字符串为NSMutableString
    • NSJSONReadingAllowFragments 允许JSON字符串最外层既不是NSArray也不是NSDictionary,但必须是有效的JSON Fragment。例如使用这个选项可以解析 @“123” 这样的字符串

    我们举个例子说明一下NSJSONReadingMutableContainers:

    NSString *str = @"{\"name\":\"zhangsan\"}";
    
    NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
    // 应用崩溃,不选用NSJSONReadingOptions,则返回的对象是不可变的,NSDictionary
    [dict setObject:@"male" forKey:@"sex"];
    
    NSMutableDictionary *dict = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
    // 没问题,使用NSJSONReadingMutableContainers,则返回的对象是可变的,NSMutableDictionary
    [dict setObject:@"male" forKey:@"sex"];
    
    NSLog(@"%@", dict);
    

    如果服务器返回的json的最外层并不是以NSArray 或者 NSDictionary ,而是一个有效的json fragment ,比如 就返回了一个@"abc"。 那么我们使用NSJSONReadingAllowFragments这个选项也能够解析出来。

    27.图片解压杂谈

    AFJSONResponseSerializer使用系统内置的NSJSONSerialization解析json,NSJSON只支持解析UTF8编码的数据(还有UTF-16LE之类的,都不常用),所以要先把返回的数据转成UTF8格式。这里会尝试用HTTP返回的编码类型和自己设置的stringEncoding去把数据解码转成字符串NSString,再把NSString用UTF8编码转成NSData,再用NSJSONSerialization解析成对象返回。

    上述过程是NSData->NSString->NSData->NSObject,这里有个问题,如果你能确定服务端返回的是UTF8编码的json数据,那NSData->NSString->NSData这两步就是无意义的,而且这两步进行了两次编解码,很浪费性能,所以如果确定服务端返回utf8编码数据,就建议自己再写个JSONResponseSerializer,跳过这两个步骤。

    此外AFJSONResponseSerializer专门写了个方法去除NSNull,直接把对象里值是NSNull的键去掉,还蛮贴心,若不去掉,上层很容易忽略了这个数据类型,判断了数据是否nil没判断是否NSNull,进行了错误的调用导致core。

    图片解压

    当我们调用UIImage的方法imageWithData:方法把数据转成UIImage对象后,其实这时UIImage对象还没准备好需要渲染到屏幕的数据,现在的网络图像PNG和JPG都是压缩格式,需要把它们解压转成bitmap后才能渲染到屏幕上,如果不做任何处理,当你把UIImage赋给UIImageView,在渲染之前底层会判断到UIImage对象未解压,没有bitmap数据,这时会在主线程对图片进行解压操作,再渲染到屏幕上。这个解压操作是比较耗时的,如果任由它在主线程做,可能会导致速度慢UI卡顿的问题。

    AFImageResponseSerializer除了把返回数据解析成UIImage外,还会把图像数据解压,这个处理是在子线程(AFNetworking专用的一条线程,详见AFURLConnectionOperation),处理后上层使用返回的UIImage在主线程渲染时就不需要做解压这步操作,主线程减轻了负担,减少了UI卡顿问题。

    具体实现上在AFInflatedImageFromResponseWithDataAtScale里,创建一个画布,把UIImage画在画布上,再把这个画布保存成UIImage返回给上层。只有JPG和PNG才会尝试去做解压操作,期间如果解压失败,或者遇到CMKY颜色格式的jpg,或者图像太大(解压后的bitmap太占内存,一个像素3-4字节,搞不好内存就爆掉了),就直接返回未解压的图像。

    另外在代码里看到iOS才需要这样手动解压,MacOS上已经有封装好的对象NSBitmapImageRep可以做这个事。

    关于图片解压,还有几个问题不清楚:

    1.本来以为调用imageWithData方法只是持有了数据,没有做解压相关的事,后来看到调用堆栈发现已经做了一些解压操作,从调用名字看进行了huffman解码,不知还会继续做到解码jpg的哪一步。
    UIImage_jpg

    2.以上图片手动解压方式都是在CPU进行的,如果不进行手动解压,把图片放进layer里,让底层自动做这个事,是会用GPU进行的解压的。不知用GPU解压与用CPU解压速度会差多少,如果GPU速度很快,就算是在主线程做解压,也变得可以接受了,就不需要手动解压这样的优化了,不过目前没找到方法检测GPU解压的速度。

    28.获取系统版本

    #ifndef NSFoundationVersionNumber_iOS_8_0
    #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11
    #else
    #define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
    #endif
    

    上边的这个宏的目的是通过NSFoundation的版本来判断当前ios版本,关键是这个宏的调试目标是IOS,来看看系统是怎么定义的:



    那么我们就能够联想到,目前我们能够判断系统版本号的方法有几种呢?最少三种:

    • [UIDevice currentDevice].systemVersion
    • 通过比较Foundation框架的版本号,iOS系统升级的同时Foundation框架的版本也会提高
    • 通过在某系版本中新出现的方法来判断,UIAlertController 这个类是iOS8之后才出现的 NS_CLASS_AVAILABLE_IOS(8_0),如果当前系统版本没有这个类NSClassFromString(@"UIAlertController" == (null),从而判断当前版本是否大于等于iOS8

    这篇博文写的很详细,关于获取当前版本

    29.dispatch_queue_create()

    AFNetworking中所有的和创建任务相关的事件都放到了一个单例的队列中,我们平时可能会使用这些方法,但还是可能会忽略一些内容,dispatch_queue_create()这个是队列的方法,第一个参数是队列的identifier,第二个参数则表示这个队列是串行队列还是并行队列。

    如果第二个参数为DISPATCH_QUEUE_SERIAL或NULL 则表示队列为串行队列。如果为DISPATCH_QUEUE_CONCURRENT则表示是并行队列。
    关于队列的小的知识点,参考了这篇文章 Objective C 高级进阶— GCD队列浅析(一).

    30.dispatch_block_t

    这个方法还有一个小知识点:dispatch_block_t ,点击去可以看到:

    typedef void (^dispatch_block_t)(void);
    

    关于这个Block我们应该注意几点:

    • 非ARC情况下,Block被allocated或者copied到堆后,一定要记得释放它,通过[release]或者Block_release()
    • 非ARC情况下,Block被allocated或者copied到堆后,一定要记得释放它,通过[release]或者Block_release()

    31.NSKeyValueObservingOptions

    • NSKeyValueObservingOptionNew 把更改之前的值提供给处理方法
    • NSKeyValueObservingOptionOld 把更改之后的值提供给处理方法
    • NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值
    • NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后

    32.task delegate出发问题

    task一共有4个delegate,只要设置了一个,就代表四个全部设置,有时候一些delegate不会被触发的原因在于这四种delegate是针对不同的URLSession类型和URLSessionTask类型来进行响应的,也就是说不同的类型只会触发这些delegate中的一部分,而不是触发所有的delegate。

    举例说明如下:

    1. 触发NSURLSessionDataDelegate

        //使用函数dataTask来接收数据
       -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
       
       //则NSURLSession部分的代码如下  
       NSURLSessionConfiguration* ephConfiguration=[NSURLSessionConfiguration defaultSessionConfiguration];
       NSURLSession* session=[NSURLSession sessionWithConfiguration:ephConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
       NSURL* url=[NSURL URLWithString:@"http://www.example.com/external_links/01.png"];  
       NSURLSessionDataTask* dataTask=[session dataTaskWithURL:url];
       [dataTask resume];
      
    2. 触发NSURLSessionDownloadDelegate

       //使用函数downloadTask来接受数据
       -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
       
       //则NSURLSession部分的代码如下
       NSURLSessionConfiguration* ephConfiguration=[NSURLSessionConfiguration defaultSessionConfiguration];
       NSURLSession* session=[NSURLSession sessionWithConfiguration:ephConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
       NSURL* url=[NSURL URLWithString:@"http://www.example.com/external_links/01.png"];  
       NSURLSessionDownloadTask* dataTask=[session downloadTaskWithURL:url];
       [dataTask resume];
      

    这两段代码的主要区别在于NSURLSessionTask的类型的不同,造成了不同的Delegate被触发.

    33.@unionOfArrays

     tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
    

    这么使用之前确实不太知道,如果是我,可能就直接赋值给数组了。那么@unionOfArrays.self又是什么意思呢?

    • @distinctUnionOfObjects 清楚重复值
    • unionOfObjects 保留重复值

    34.相对路径(relative)

    假如有一个基础路径NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];我们暂时命名为baseURL.所谓 相对 肯定跟这个baseURL有关系

    我们可以通过 NSURL +URLWithString:relativeToUL:这个方法来获取一个路径,至于怎么使用,我们通过一个例子来说明:

    NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
    [NSURL URLWithString:@"foo" relativeToURL:baseURL];                  // http://example.com/v1/foo
    [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL];          // http://example.com/v1/foo?bar=baz
    [NSURL URLWithString:@"/foo" relativeToURL:baseURL];                 // http://example.com/foo
    [NSURL URLWithString:@"foo/" relativeToURL:baseURL];                 // http://example.com/v1/foo
    [NSURL URLWithString:@"/foo/" relativeToURL:baseURL];                // http://example.com/foo/
    [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
    

    至于 绝对路径相对路径 的定义,使用方法,优缺点,这里就不提了,大家可以自行了解。说一下为什么使用相对路径吧。

    在真实开发中,一般都会有一个线上的服务器和一下测试服务器,当然也可能多个。在ios开发中切换开发环境有好几种方法

    • 通过target
    • 自定义一个字段HTTPURL,用它来控制路径
    • 通过相对路径来切换接口

    35.dispatch_barrier_async

    barrier 这个单词的意思是障碍,拦截的意思,也即是说 dispatch_barrier_async 一定是有拦截事件的作用。

    看下边这段代码:

    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-1");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-2");
        });
        dispatch_barrier_async(concurrentQueue, ^(){
            NSLog(@"dispatch-barrier");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-3");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-4");
        });
    

    打印结果:

    2016-08-22 16:43:20.554 xxx[26805:271426] dispatch-1
    2016-08-22 16:43:20.555 xxx[26805:271422] dispatch-2
    2016-08-22 16:43:20.556 xxx[26805:271422] dispatch-barrier
    2016-08-22 16:43:20.556 xxx[26805:271422] dispatch-3
    2016-08-22 16:43:20.556 xxx[26805:271426] dispatch-4
    

    这个说明了 dispatch_barrier_async 能够拦截它前边的异步事件,等待两个异步方法都完成之后,调用 dispatch_barrier_async

    我们稍微改动一下:

    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-1");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-2");
    });
    dispatch_barrier_sync(concurrentQueue, ^(){
        NSLog(@"dispatch-barrier");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-3");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-4");
    });
    

    打印结果:

    2016-08-22 16:43:20.554 xxx[26805:271426] dispatch-1
    2016-08-22 16:43:20.555 xxx[26805:271422] dispatch-2
    2016-08-22 16:43:20.556 xxx[26805:271422] dispatch-barrier
    2016-08-22 16:43:20.556 xxx[26805:271422] dispatch-3
    2016-08-22 16:43:20.556 xxx[26805:271426] dispatch-4
    

    36.dispatch_sync() 和 dispatch_async()

    大概说下 dispatch_sync() 和 dispatch_async() 这两个方法。

    示例代码:

    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        dispatch_sync(concurrentQueue, ^(){
            NSLog(@"2");
            [NSThread sleepForTimeInterval:5];
            NSLog(@"3");
        });
        NSLog(@"4");
    

    输出为:

    2016-08-25 11:50:51.601 xxxx[1353:102804] 1
    2016-08-25 11:50:51.601 xxxx[1353:102804] 2
    2016-08-25 11:50:56.603 xxxx[1353:102804] 3
    2016-08-25 11:50:56.603 xxxx[1353:102804] 4
    

    再看:

    dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1");
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"2");
            [NSThread sleepForTimeInterval:5];
            NSLog(@"3");
        });
        NSLog(@"4");
    

    输出为:

    2016-08-25 11:52:29.022 xxxx[1392:104246] 1
    2016-08-25 11:52:29.023 xxxx[1392:104246] 4
    2016-08-25 11:52:29.023 xxxx[1392:104284] 2
    2016-08-25 11:52:34.029 xxxx[1392:104284] 3
    

    通过上边的两个例子,我们可以总结出:

    • dispatch_sync(),同步添加操作。他是等待添加进队列里面的操作完成之后再继续执行
    • dispatch_async ,异步添加进任务队列,它不会做任何等待

    37.NSURLCache

    我们简单介绍下NSURLCache。NSURLCache 为您的应用的 URL 请求提供了内存中以及磁盘上的综合缓存机制。网络缓存减少了需要向服务器发送请求的次数,同时也提升了离线或在低速网络中使用应用的体验。当一个请求完成下载来自服务器的回应,一个缓存的回应将在本地保存。下一次同一个请求再发起时,本地保存的回应就会马上返回,不需要连接服务器。NSURLCache 会 自动 且 透明 地返回回应。

    为了好好利用 NSURLCache,你需要初始化并设置一个共享的 URL 缓存。在 iOS 中这项工作需要在 -application:didFinishLaunchingWithOptions: 完成,而 OS X 中是在 –applicationDidFinishLaunching::

    示例代码:

    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
      NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                           diskCapacity:20 * 1024 * 1024
                                                               diskPath:nil];
      [NSURLCache setSharedURLCache:URLCache];
    }
    

    NSURLRequest 有个 cachePolicy 属性,我们平时最常用的有四个属性:

    • NSURLRequestUseProtocolCachePolicy: 对特定的 URL 请求使用网络协议中实现的缓存逻辑。这是默认的策略
    • NSURLRequestReloadIgnoringLocalCacheData:数据需要从原始地址加载。不使用现有缓存。
    • NSURLRequestReturnCacheDataElseLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么从原始地址加载数据
    • NSURLRequestReturnCacheDataDontLoad:无论缓存是否过期,先使用本地缓存数据。如果缓存中没有请求所对应的数据,那么放弃从原始地址加载数据,请求视为失败(即:“离线”模式)。

    38.@synchronized()锁

    synchronized是一种锁,这种锁不管是在oc中还是java中用的都挺多的,而且这种锁锁得是对象。具体原理,可以看这篇文章后边的 参考 那一部分。
    总结一下,锁一般用于多线程环境下对数据的操作中。在 AFNetworking 中我们见到了3种不同的锁,分别是:

    1. NSLock
    2. dispatch_semaphore_wait
    3. @synchronized

    39.UIWebView+AFNetworking

    UIWebView的这个分类是这几个分类中最让我惊讶的一个。让我真正认识到条条大路通罗马到底是什么意思。有时候人的思想确实会被固有的思维所束缚。这里只是用了UIWebView的loadData:(NSData )data MIMEType:(NSString )MIMEType textEncodingName:(NSString )textEncodingName baseURL:(NSURL )baseURL方法

    你会发现使用这个分类配合UIWebView,所有的事情都变得很简单。

    示例代码

    - (void)loadRequest:(NSURLRequest *)request
               MIMEType:(NSString *)MIMEType
       textEncodingName:(NSString *)textEncodingName
               progress:(NSProgress * _Nullable __autoreleasing * _Nullable)progress
                success:(NSData * (^)(NSHTTPURLResponse *response, NSData *data))success
                failure:(void (^)(NSError *error))failure
    {
        // 检查参数
        NSParameterAssert(request);
    
        // 如果正处于运行或者暂停装状态,就取消之前的任务task并设置为nil
        if (self.af_URLSessionTask.state == NSURLSessionTaskStateRunning || self.af_URLSessionTask.state == NSURLSessionTaskStateSuspended) {
            [self.af_URLSessionTask cancel];
        }
        self.af_URLSessionTask = nil;
    
        __weak __typeof(self)weakSelf = self;
        NSURLSessionDataTask *dataTask;
        dataTask = [self.sessionManager
                GET:request.URL.absoluteString
                parameters:nil
                progress:nil
                success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
                    __strong __typeof(weakSelf) strongSelf = weakSelf;
                    
                    // 请求成功后,调用success block
                    if (success) {
                        success((NSHTTPURLResponse *)task.response, responseObject);
                    }
                    // 显示数据
                    [strongSelf loadData:responseObject MIMEType:MIMEType textEncodingName:textEncodingName baseURL:[task.currentRequest URL]];
    
                    // 调用webViewDidFinishLoad
                    if ([strongSelf.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
                        [strongSelf.delegate webViewDidFinishLoad:strongSelf];
                    }
                }
                failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
                    if (failure) {
                        failure(error);
                    }
                }];
        self.af_URLSessionTask = dataTask;
        
        // 设置progress,这个来自于self.sessionManager
        if (progress != nil) {
            *progress = [self.sessionManager downloadProgressForTask:dataTask];
        }
        
        // 开启任务
        [self.af_URLSessionTask resume];
    
        // 调用webViewDidStartLoad方法
        if ([self.delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
            [self.delegate webViewDidStartLoad:self];
        }
    }
    

    总结

    AFNetworking 的源码解读到此就结束了。我曾经说要写一个网络框架,但随着对网络的更进一步的了解。一个好的框架不是随随便便写的。需要对使用者负责。因此我又找了几个目前比较流行的框架,对他们的思想研究研究。实属拿来主义。

    相关文章

      网友评论

      • Beta是条好狗:@distinctUnionOfObjects 清楚重复值
        -----------
        清除
        Beta是条好狗:@老马的春天 其实之前还看到一些都没有这样回复,难得让作者现身,之后再看到类似的地方就毫不客气的留言指出好了 :sweat_smile:
        老马的春天:@weiping_lii :smile:编程天才啊你是,如此仔细
      • 半碗大米汤:能写读书笔记已经值得赞一个

      本文标题:AFNetworking 3.0 源码解读 总结(干货)(下)

      本文链接:https://www.haomeiwen.com/subject/cqtkettx.html