AFNetworking 3.x 阅读笔记(三)

前面两篇笔记已经介绍了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  *tasks;

/**
 The data tasks currently run by the managed session.
 */
@property (readonly, nonatomic, strong) NSArray  *dataTasks;

/**
 The upload tasks currently run by the managed session.
 */
@property (readonly, nonatomic, strong) NSArray  *uploadTasks;

/**
 The download tasks currently run by the managed session.
 */
@property (readonly, nonatomic, strong) NSArray  *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 阅读笔记(三))