• AFNetworking 源码解读


    最近开始看第三方库优秀源码的计划,这是第一个,AFNetworking来和大家分享一下。

    AFNetworking 是一个十分优秀的网络框架,简单易用。 在开始之前,最好先了解一下NSURLSession相关的知识。详情可看http://www.cnblogs.com/bigly/p/8476610.html。

    首先,我们来看一下AFNetworking的文件结构:

     从图中我们可以看出,AF大致分为五个模块:

    网络请求模块,  (AFURLSessionManager, AFHTTPSessionManager)

    网络状态模块,   (AFNetworkReachabilityManager)

    安全策略模块,   (AFSecurityPolicy)

    序列化反序列化模块,(AFURLRequestSerialization, AFURLResponseSerialization)

    UI相关模块

    其中,最重要的是网络请求模块。

    现在我们来看看AFNetworking怎么使用的,以下是作者git上的源代码:

    网络请求:

    下载:

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    
    NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
        NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
        return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
        NSLog(@"File downloaded to: %@", filePath);
    }];
    [downloadTask resume];

    通过这段代码,我们可以看到,AFNetworking 是通过对NSURLSession进行封装来实现的。我们先来看一下AFURLSessionManager的初始化,在初始化方法里AF并没有做太多事,主要是NSURLSession的初始化以及安全策略等初始化。(关于安全策略和序列化我们后面再进行讲解)

    - (instancetype)init {
        return [self initWithSessionConfiguration:nil];
    }
    
    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
    
        self.sessionConfiguration = configuration;
    
        //初始化任务所在队列,且设置并发数为1
        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.maxConcurrentOperationCount = 1;
        
        //初始化NSURLSession
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    
        //初始化Serializer
        self.responseSerializer = [AFJSONResponseSerializer serializer];
        
        //初始化安全策略
        self.securityPolicy = [AFSecurityPolicy defaultPolicy];
    
        //初始化网络监听模块
    #if !TARGET_OS_WATCH
        self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    #endif
    
        //初始化代理相关的字典
        self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
        
        //初始化锁
        self.lock = [[NSLock alloc] init];
        self.lock.name = AFURLSessionManagerLockName;
        
        /*初始化时获取所有进行中的任务,并将所有代理置空。 正常来说,初始化时应该获取不到任何的task, 事实上也是如此,但在
         进入后台再到前台初始化化时可能会有任务,导致崩溃
         */
        
        [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;
    }

    接下来看一下NSURLSessionDownloadTask 的获取方法。

    - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                                 progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                              destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                        completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
    {
        __block NSURLSessionDownloadTask *downloadTask = nil;
        
        //同步队列生成task
        url_session_manager_create_task_safely(^{
            downloadTask = [self.session downloadTaskWithRequest:request];
        });
        
        //添加对应的AFURLSessionManagerTaskDelegate
        [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
    
        return downloadTask;
    }

    这里有一个比较巧妙的地方,AFURLSessionManager实现了NSURLSession相关的代理方法,然后在每次生成task时,将传入的block以字典形式持有,

    然后在NSURLSession代理方法中调用,这样使用者就不需要去实现相关的代理,只需要传入block,更加简洁。

    - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                              progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                           destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                     completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
    {
        //创建代理对象
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
        delegate.manager = self;
        
        //设置对应的block
        delegate.completionHandler = completionHandler;
    
        if (destination) {
            delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
                return destination(location, task.response);
            };
        }
    
        downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
    
        //将代理对象添加到字典中
        [self setDelegate:delegate forTask:downloadTask];
    
        delegate.downloadProgressBlock = downloadProgressBlock;
    }

    此外,在添加代理对象的时候,AF增加了一些额外的监听方法,使用rutime实现,实现方式比较巧妙,有兴趣的可以研究一下

    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        [self.lock lock];
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        //监听task,使用runtime来实现
        [self addNotificationObserverForTask:task];
        [self.lock unlock];
    }

    获取到NSURLSessionDownloadtask之后直接调用resume方法开始下载,接下来我们看看回调相关的方法。

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        //从字典获取对应的代理对象
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
        
        //如果设置了downloadTask处理的block则执行该block
        //一般比较少采用这种方式
        if (self.downloadTaskDidFinishDownloading) {
            
            //获取文件保存路径
            NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
            if (fileURL) {
                delegate.downloadFileURL = fileURL;
                NSError *error = nil;
                
                //对于downloadtask,下载完成后需要将文件拷贝到其他路径否则会被删除
                if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
                }
    
                return;
            }
        }
    
        //使用代理对象执行对应方法,和手动设置block逻辑一样
        if (delegate) {
            [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
        }
    }

    在下载完成的回调方法中,我们看到有两条路径,一个是从字典中获取对应的代理对象,另外一种是直接获取下载完成回调的block,

    这两种方式大同小异,都是在回调方法中做了一层转接,如果要直接使用下载完成的block可以通过以下方法设置。

    - (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable  (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *locatio

    NSURLSessionDownloadTaskDelegate 还有很多其他回调方法,但原理同上,在此不一一赘述。

    NSURLSessionDataTask,NSURLSessionUploadTask等和NSURLSessionDownLoadTask的使用类似,也不一一讲解。

    关于网络请求,我们可以看到还有一个AFHTTPSessionManager, 顾名思义,这个类是专门为HTTP请求封装的。它继承自AFHTTPSessionManager

    对比一下AFURLSessionManager,我们可以发现,在AFHTTPSessionManager有一个属性是AFURLSessionManager所没有的。

    @property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

    这个类负责构造各种http请求,而实际发送请求的还是在AFURLSessionManger中的相关方法。

    后续讲解AFURLRequestSerialization时会提到。//ps:先占个坑,有时间继续完成

  • 相关阅读:
    PS转换图片——我教你
    通过Ajax——异步获取相关问题解答
    Spring的线程安全
    Spring MVC的工作机制
    Annotation的语法和使用
    Spring Bean的生命周期
    浅谈Spring
    Spring的事务管理
    行为型模式
    结构型模式
  • 原文地址:https://www.cnblogs.com/bigly/p/8511540.html
Copyright © 2020-2023  润新知