美文网首页iOS第三方库
AFNetworking 3.x 阅读笔记(三)

AFNetworking 3.x 阅读笔记(三)

作者: brownfeng | 来源:发表于2016-09-17 23:44 被阅读211次

    前面两篇笔记已经介绍了AFHTTPSessionManager,本文将会深入到整个网络库的核心类AFURLSessionManager.

    我们指定AFNetworking3.x使用的是NSURLSession,根据请求的不同通过它创建不同的task(dataTaskXXX,uploadTaskXXX,downloadTaskXXX),不论是session层还是task层,产生的事件都需要异步通过delegate处理.相关的delegate:

    • NSURLSessionDelegate
    • NSURLSessionTaskDelegate
    • NSURLSessionDataDelegate
    • NSURLSessionDownloadDelegate

    AFURLSessionManager就充当着管理session,创建task,处理上述的各种delegate事件的作用.

    核心类的初始化

    AFURLSessionManager的指定init方法中:

    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        // 1 创建session的配置类
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
        self.sessionConfiguration = configuration;
        // 2 创建session 的delegate运行的queue
        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.maxConcurrentOperationCount = 1;
        // 3 创建session实例,并且强引用,delegate 是自己
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
        // 4 创建response 接受的数据序列化的辅助类,默认接受JSON数据
        self.responseSerializer = [AFJSONResponseSerializer serializer];
        // 5 配置安全策略 -> 主要在delegate中收到鉴权请求时候使用
        self.securityPolicy = [AFSecurityPolicy defaultPolicy];
    
    #if !TARGET_OS_WATCH
        // 6 创建网络状态监听类 -> 但是实际中框架里面好像没怎么使用???
        self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    #endif
    
        // 7 创建dict,用来管理task对应的delegate,因为有部分内容交给 delegate helper 类去完成的
        self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
        // 8 [task:delegateHelper] 访问时候的锁
        self.lock = [[NSLock alloc] init];
        self.lock.name = AFURLSessionManagerLockName;
    
        // 9 为什么要调用这个方法??????有点奇怪-> 方法的主要作用是获取session中所有的真该执行的tasks
        [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;
    }
    

    task delegate helper类

    几乎核心类所有的方法都是围绕session的delegate方法展开的.不论是session层的delegate,还是session产生的task层的delegate方法的实现都在AFURLSessionManager实现.

    为了区分不同task产生的delegate事件,AFNetworking引入了AFURLSessionManagerTaskDelegate类.该类主要帮助每个task完成两大块的任务:

    • 1 task执行下载完成任务以后的completionHanlder由delegate helper类执行的
    • 2 在以下几个delegate中帮助特定的task处理delegate事件
    • 3 完成关于NSProgress相关上传或者下载的进度的progressBlock
    // NSURLSessionTaskDelegate, 这个帮助方法也是dataTask完成请求以后,responseSerializer的入口
    - (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
    
    // NSURLSessionDataDelegate
    - (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data;
    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
    
    //NSURLSessionDownloadDelegate
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes;
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location;
    

    再了解框架是如何使用该类来帮助完成这两块任务之前,我们先来看下AFURLSessionManager是如何使用该类来绑定不同的task的.

    AFURLSessionManagerdataTaskWithRequest:xxxxx方法使用session创建Task(dataTask,uploadTask,downloadTask)以后,都会调用该类的另外一簇方法,将刚刚创建的task实例当做参数传递进去.

    下面是addDelegateForXXXTask:XXXProgress:...:completionHanlder方法:

    - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                    uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                  downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
    
    - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                            progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                   completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
    
    - (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;
    

    为了说明delegate helper 是如何与task绑定,又如何在代理方法中分发的,这里举一个简单的例子:

    该方法簇是创建一个该类的实例,然后将它与刚刚创建的task关联起来,存放到AFURLSessionManagermutableTaskDelegatesKeyedByTaskIdentifier的NSDictionary实例中,当调用上文提到的几个delegate方法时候,就会根据不同的task调用不同task.identifier为key的AFURLSessionManagerTaskDelegate对象完成处理.简而言之,AFURLSessionManager就犹如一个路由,给不同的task任务派发不同的事件.具体事件的处理函数都在各自task对应的task delegate helper方法中去完成,具体的实例代码如下:

    /**
     *  将dataTask和一个 AFURLSessionManagerTaskDelegate 关联起来,将其中部分progressBlock交给delegate去管理
     */
    - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                    uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                  downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
    {
    // 1 创建task delegate helper 类
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
    // 2 delegate helper 也会在task收到执行完毕的回调以后执行completionHanlder
        delegate.completionHandler = completionHandler;
    // 3 用来标识task唯一性!!!!用的task的内存地址,将dataTask <--> delegate helper关联起来
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
        [self setDelegate:delegate forTask:dataTask];
    // 4 NSProgress相关的Block都是由delegate helper去更新的
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    
    // 通知delegate, task已经完成, 会直接调用 taskDelegateHelper -> 将数据通过responseSerializer处理
    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        // 1 获取delegate -> 2 调用delegate 的相似方法 -> 3 从dict中删除(删除以后,注意清理现场)
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
    
        // delegate may be nil when completing a task in the background
        if (delegate) {
            // 2 使用task对应的delegate helper 执行该代理任务
            [delegate URLSession:session task:task didCompleteWithError:error];
            // 3 将task从[task:delegate]中删除
            [self removeDelegateForTask:task];
        }
    
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    

    task delegate helper 的存取

    由于session创建的每个task都有一个对应的AFURLSessionManagerTaskDelegate,因此在访问manager的mutableTaskDelegatesKeyedByTaskIdentifier字典时候,不可避免的遇到字典的读写同步问题.框架中通过一个NSLock保证读写时可能面临的同步问题.对数据只有 -- 增, 删, 查

    // 增
    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        // 访问 mutableTaskDelegatesKeyedByTaskIdentifier 字典时候需要加锁,在其他存储时候也需要加锁
        [self.lock lock];
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;//使用task的identifier作为key
        [delegate setupProgressForTask:task];// 为task设置NSProgress的重要内容
        [self addNotificationObserverForTask:task];// 监听task完成或者恢复时候发送的通知
        [self.lock unlock];
    }
    // 删
    - (void)removeDelegateForTask:(NSURLSessionTask *)task {
        NSParameterAssert(task);
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
        [self.lock lock];
        [delegate cleanUpProgressForTask:task];
        [self removeNotificationObserverForTask:task];
        [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
        [self.lock unlock];
    }
    
    // 改 -> 没有对task进行修改 -> 代码里面用的先删除然后设置的方法
    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        if (delegate) {
            [self removeDelegateForTask:dataTask];
            [self setDelegate:delegate forTask:downloadTask];
        }
    
        if (self.dataTaskDidBecomeDownloadTask) {
            self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
        }
    }
    
    // 查
    - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
        NSParameterAssert(task);
    
        AFURLSessionManagerTaskDelegate *delegate = nil;
        // 使用加锁的方式获取task对应的delegate
        [self.lock lock];
        delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];// 通过taskIdentifier完成
        [self.lock unlock];
    
        return delegate;
    }
    

    其他学习的地方

    AFURLSessionManager中除了delegate方法,还有一组关于正在运行的session task属性的方法簇:

    ///----------------------------
    /// @name Getting Session Tasks
    ///----------------------------
    
    /**
     The data, upload, and download tasks currently run by the managed session.
     */
    @property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
    
    /**
     The data tasks currently run by the managed session.
     */
    @property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
    
    /**
     The upload tasks currently run by the managed session.
     */
    @property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
    
    /**
     The download tasks currently run by the managed session.
     */
    @property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
    

    这些方法簇的getter实现的也比较巧妙:

    - (NSArray *)tasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];// _cmd 表示方法名称
    }
    
    - (NSArray *)dataTasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    
    - (NSArray *)uploadTasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    
    - (NSArray *)downloadTasks {
        return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
    }
    
    // 通过keyPath 获取对应的tasks array. -> 使用dispatch_semephore_t 等待异步调用完成,然后执行的方法
    - (NSArray *)tasksForKeyPath:(NSString *)keyPath {
        __block NSArray *tasks = nil;
        // 利用信号量异步等待的方法!!!!
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {// 异步调用block
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
                tasks = dataTasks;
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
                tasks = uploadTasks;
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
                tasks = downloadTasks;
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
                tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
            }
    
            dispatch_semaphore_signal(semaphore);
        }];
    
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
        return tasks;
    }
    

    其中,在tasksForKeyPath方法中犹豫,session获取正在执行的tasks是使用的异步block返回查询结果的.因此框架中使用了dispatch_semaphore_t来等待异步结果,可以在以后工作中学以致用:

    // 1 创建信号量 -> 初始计数是0
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    block(){//异步block方法中给信号量发信息
            ...
             // 2 在block异步结果返回时候发送信号
            dispatch_semaphore_signal(semaphore);
         }
    
    //3 等待信号量的数据以后进行后面内容的执行
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    ...
    

    相关文章

      网友评论

        本文标题:AFNetworking 3.x 阅读笔记(三)

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