AFURLSessionManager(二)

建议去看原文  AFURLSessionManager

_AFURLSessionTaskSwizzling

当时看这个私有类的时候一直想不通为什么要弄一个这样的类呢?首先看了AFNetworking给出的解释https://github.com/AFNetworking/AFNetworking/pull/2702大概说了当初这个私有类的由来,ios7和ios8 task的父类并不一样,关键是resumeandsuspend这两个方法的调用。

因此,AFNetworking 利用Runtime交换了resumeandsuspend的方法实现。在替换的方法中发送了状态的通知。这个通知被使用在UIActivityIndicatorView+AFNetworking这个UIActivityIndicatorView的分类中。

方法的核心部分作用是层级遍历父类,替换resumeandsuspend的实现方法。同时也解决了锁死这个bug。

还有值得说的是+ (void)load这个方法,这个方法会在app启动时加载所有类的时候调用,且只会调用一次,所以这就有了使用场景了,当想使用运行时做一些事情的时候,就能够用上这个方法了。

举几个使用这个方法的例子:

SDAutoLayout

IQKeyBoardManager

UITableView+FDTemplateLayoutCell

MJExtension

下边就看看代码部分:

// 根据两个方法名称交换两个方法,内部实现是先根据函数名获取到对应方法实现// 再调用method_exchangeImplementations交换两个方法staticinlinevoidaf_swizzleSelector(Class theClass, SEL originalSelector, SEL swizzledSelector){    Method originalMethod = class_getInstanceMethod(theClass, originalSelector);    Method swizzledMethod = class_getInstanceMethod(theClass, swizzledSelector);    method_exchangeImplementations(originalMethod, swizzledMethod);}// 给theClass添加名为selector,对应实现为method的方法staticinlineBOOLaf_addMethod(Class theClass, SEL selector, Method method){// 内部实现使用的是class_addMethod方法,注意method_getTypeEncoding是为了获得该方法的参数和返回类型returnclass_addMethod(theClass, selector,  method_getImplementation(method),  method_getTypeEncoding(method));}

--

+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {// 因为af_resume和af_suspend都是类的实例方法,所以使用class_getInstanceMethod获取这两个方法Method afResumeMethod = class_getInstanceMethod(self,@selector(af_resume));    Method afSuspendMethod = class_getInstanceMethod(self,@selector(af_suspend));// 给theClass添加一个名为af_resume的方法,使用@selector(af_resume)获取方法名,使用afResumeMethod作为方法实现if(af_addMethod(theClass,@selector(af_resume), afResumeMethod)) {// 交换resume和af_resume的方法实现af_swizzleSelector(theClass,@selector(resume),@selector(af_resume));    }// 同上if(af_addMethod(theClass,@selector(af_suspend), afSuspendMethod)) {        af_swizzleSelector(theClass,@selector(suspend),@selector(af_suspend));    }}

--

- (NSURLSessionTaskState)state {NSAssert(NO,@"State method should never be called in the actual dummy class");// 初始状态是NSURLSessionTaskStateCanceling;returnNSURLSessionTaskStateCanceling;}- (void)af_resume {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];    [selfaf_resume];// 因为经过method swizzling后,此处的af_resume其实就是之前的resume,所以此处调用af_resume就是调用系统的resume。但是在程序中我们还是得使用resume,因为其实际调用的是af_resume// 如果之前是其他状态,就变回resume状态,此处会通知调用taskDidResumeif(state !=NSURLSessionTaskStateRunning) {        [[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotificationobject:self];    }}// 同上- (void)af_suspend {NSAssert([selfrespondsToSelector:@selector(state)],@"Does not respond to state");NSURLSessionTaskStatestate = [selfstate];    [selfaf_suspend];if(state !=NSURLSessionTaskStateSuspended) {        [[NSNotificationCenterdefaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotificationobject:self];    }}

--

+ (void)load {/**

WARNING: 高能预警

https://github.com/AFNetworking/AFNetworking/pull/2702

*/// 担心以后iOS中不存在NSURLSessionTaskif(NSClassFromString(@"NSURLSessionTask")) {/**

iOS 7和iOS 8在NSURLSessionTask实现上有些许不同,这使得下面的代码实现略显trick

关于这个问题,大家做了很多Unit Test,足以证明这个方法是可行的

目前我们所知的:

- NSURLSessionTasks是一组class的统称,如果你仅仅使用提供的API来获取NSURLSessionTask的class,并不一定返回的是你想要的那个(获取NSURLSessionTask的class目的是为了获取其resume方法)

- 简单地使用[NSURLSessionTask class]并不起作用。你需要新建一个NSURLSession,并根据创建的session再构建出一个NSURLSessionTask对象才行。

- iOS 7上,localDataTask(下面代码构造出的NSURLSessionDataTask类型的变量,为了获取对应Class)的类型是 __NSCFLocalDataTask,__NSCFLocalDataTask继承自__NSCFLocalSessionTask,__NSCFLocalSessionTask继承自__NSCFURLSessionTask。

- iOS 8上,localDataTask的类型为__NSCFLocalDataTask,__NSCFLocalDataTask继承自__NSCFLocalSessionTask,__NSCFLocalSessionTask继承自NSURLSessionTask

- iOS 7上,__NSCFLocalSessionTask和__NSCFURLSessionTask是仅有的两个实现了resume和suspend方法的类,另外__NSCFLocalSessionTask中的resume和suspend并没有调用其父类(即__NSCFURLSessionTask)方法,这也意味着两个类的方法都需要进行method swizzling。

- iOS 8上,NSURLSessionTask是唯一实现了resume和suspend方法的类。这也意味着其是唯一需要进行method swizzling的类

- 因为NSURLSessionTask并不是在每个iOS版本中都存在,所以把这些放在此处(即load函数中),比如给一个dummy class添加swizzled方法都会变得很方便,管理起来也方便。

一些假设前提:

- 目前iOS中resume和suspend的方法实现中并没有调用对应的父类方法。如果日后iOS改变了这种做法,我们还需要重新处理

- 没有哪个后台task会重写resume和suspend函数

*/// 1) 首先构建一个NSURLSession对象session,再通过session构建出一个_NSCFLocalDataTask变量NSURLSessionConfiguration*configuration = [NSURLSessionConfigurationephemeralSessionConfiguration];NSURLSession* session = [NSURLSessionsessionWithConfiguration:configuration];#pragma GCC diagnostic push#pragma GCC diagnostic ignored"-Wnonnull"NSURLSessionDataTask*localDataTask = [session dataTaskWithURL:nil];#pragma clang diagnostic pop// 2) 获取到af_resume实现的指针IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([selfclass],@selector(af_resume)));        Class currentClass = [localDataTask class];// 3) 检查当前class是否实现了resume。如果实现了,继续第4步。while(class_getInstanceMethod(currentClass,@selector(resume))) {// 4) 获取到当前class的父类(superClass)Class superClass = [currentClass superclass];// 5) 获取到当前class对于resume实现的指针IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass,@selector(resume)));//  6) 获取到父类对于resume实现的指针IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass,@selector(resume)));// 7) 如果当前class对于resume的实现和父类不一样(类似iOS7上的情况),并且当前class的resume实现和af_resume不一样,才进行method swizzling。if(classResumeIMP != superclassResumeIMP &&                originalAFResumeIMP != classResumeIMP) {                [selfswizzleResumeAndSuspendMethodForClass:currentClass];            }// 8) 设置当前操作的class为其父类class,重复步骤3~8currentClass = [currentClass superclass];        }                [localDataTask cancel];        [session finishTasksAndInvalidate];    }}

AFURLSessionManager

这个类的属性我们就不解释了,代码也不贴上来了。我们来看看初始化方法中都设置了那些默认的值:

- (instancetype)init {return[selfinitWithSessionConfiguration:nil];}- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration*)configuration {self= [superinit];if(!self) {returnnil;    }if(!configuration) {        configuration = [NSURLSessionConfigurationdefaultSessionConfiguration];    }self.sessionConfiguration = configuration;self.operationQueue = [[NSOperationQueuealloc] init];self.operationQueue.maxConcurrentOperationCount =1;self.session = [NSURLSessionsessionWithConfiguration:self.sessionConfiguration delegate:selfdelegateQueue:self.operationQueue];self.responseSerializer = [AFJSONResponseSerializer serializer];self.securityPolicy = [AFSecurityPolicy defaultPolicy];#if !TARGET_OS_WATCHself.reachabilityManager = [AFNetworkReachabilityManager sharedManager];#endifself.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionaryalloc] init];self.lock = [[NSLockalloc] init];self.lock.name = AFURLSessionManagerLockName;    [self.session getTasksWithCompletionHandler:^(NSArray*dataTasks,NSArray*uploadTasks,NSArray*downloadTasks) {for(NSURLSessionDataTask*taskindataTasks) {            [selfaddDelegateForDataTask:task uploadProgress:nildownloadProgress:nilcompletionHandler:nil];        }for(NSURLSessionUploadTask*uploadTaskinuploadTasks) {            [selfaddDelegateForUploadTask:uploadTask progress:nilcompletionHandler:nil];        }for(NSURLSessionDownloadTask*downloadTaskindownloadTasks) {            [selfaddDelegateForDownloadTask:downloadTask progress:nildestination:nilcompletionHandler:nil];        }    }];returnself;}- (void)dealloc {    [[NSNotificationCenterdefaultCenter] removeObserver:self];}

可以看出默认创建一个NSOperationQueue且并发数为一个,默认的responseSerializer响应序列化为Json,默认的securityPolicy为defaultPolicy,同时添加reachabilityManager网络监控对象。

- (NSString*)taskDescriptionForSessionTasks {return[NSStringstringWithFormat:@"%p",self];}

这个方法返回一个本类的地址,目的是通过这个字符串来判断请求是不是来源于AFNetworkingAFNetworking在为每个task添加Delegate的时候,都会给task的taskDescription赋值为self.taskDescriptionForSessionTasks。在后边的- (NSArray *)tasksForKeyPath:(NSString *)keyPath方法中会使用到这个字符串。

- (void)taskDidResume:(NSNotification*)notification {NSURLSessionTask*task = notification.object;if([task respondsToSelector:@selector(taskDescription)]) {if([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {dispatch_async(dispatch_get_main_queue(), ^{                [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidResumeNotification object:task];            });        }    }}- (void)taskDidSuspend:(NSNotification*)notification {NSURLSessionTask*task = notification.object;if([task respondsToSelector:@selector(taskDescription)]) {if([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks]) {dispatch_async(dispatch_get_main_queue(), ^{                [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidSuspendNotification object:task];            });        }    }}

这两个是通知方法,来源于下边的两个通知的监听事件:

- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {    [[NSNotificationCenter defaultCenter]addObserver:selfselector:@selector(taskDidResume:)name:AFNSURLSessionTaskDidResumeNotificationobject:task];    [[NSNotificationCenter defaultCenter]addObserver:selfselector:@selector(taskDidSuspend:)name:AFNSURLSessionTaskDidSuspendNotificationobject:task];}- (void)removeNotificationObserverForTask:(NSURLSessionTask *)task {    [[NSNotificationCenter defaultCenter]removeObserver:selfname:AFNSURLSessionTaskDidSuspendNotificationobject:task];    [[NSNotificationCenter defaultCenter]removeObserver:selfname:AFNSURLSessionTaskDidResumeNotificationobject:task];}

还记得上边提到的**_AFURLSessionTaskSwizzling**这个私有类吗?它交换了resumeandsuspend这两个方法,在方法中发了下边两个通知:

AFNSURLSessionTaskDidResumeNotification

AFNSURLSessionTaskDidSuspendNotification

接下来就是一个很巧妙的转化过程了,按理说我们只需要接受并处理上边的两个通知不就可以了吗? 但真实情况却不是这样的,并不是所有人使用网络请求都是用AFNetworking,所以使用if ([task.taskDescription isEqualToString:self.taskDescriptionForSessionTasks])来做判断,这个task是否来自AFNetworking

转化后我们就是用下边的通知,同时也是对外暴露出来的通知:

AFNetworkingTaskDidResumeNotification

AFNetworkingTaskDidSuspendNotification

- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {    NSParameterAssert(task);    AFURLSessionManagerTaskDelegate *delegate= nil;    [self.locklock];delegate= self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];    [self.lockunlock];returndelegate;}- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegateforTask:(NSURLSessionTask *)task{    NSParameterAssert(task);    NSParameterAssert(delegate);    [self.locklock];    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] =delegate;    [delegatesetupProgressForTask:task];    [self addNotificationObserverForTask:task];    [self.lockunlock];}

这两个方法是把AFURLSessionManagerTaskDelegate和task建立联系。值得注意的是:

self.mutableTaskDelegatesKeyedByTaskIdentifier 这个字典以task.taskIdentifier为key,delegate为value。同事在读取和设置的时候采用加锁来保证安全。

在给task添加delegate的时候除了给self.mutableTaskDelegatesKeyedByTaskIdentifier赋值外,还需要设置delegate的ProgressForTask,且添加task的通知

--

- (void)addDelegateForDataTask:(NSURLSessionDataTask*)dataTask                uploadProgress:(nullablevoid(^)(NSProgress*uploadProgress)) uploadProgressBlock              downloadProgress:(nullablevoid(^)(NSProgress*downloadProgress)) downloadProgressBlock            completionHandler:(void(^)(NSURLResponse*response,idresponseObject,NSError*error))completionHandler{    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];    delegate.manager =self;    delegate.completionHandler = completionHandler;    dataTask.taskDescription =self.taskDescriptionForSessionTasks;    [selfsetDelegate:delegate forTask:dataTask];    delegate.uploadProgressBlock = uploadProgressBlock;    delegate.downloadProgressBlock = downloadProgressBlock;}

给datatask添加delegate,AFNetworking中的每一个task肯定都有一个delegate。根据这个方法,我们可以看出给task添加代理的步骤为:

新建AFURLSessionManagerTaskDelegate

设置delegate

设置taskDescription

把taskdelegateAFURLSessionManager建立联系

--

- (void)removeDelegateForTask:(NSURLSessionTask*)task {NSParameterAssert(task);    AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:task];    [self.lock lock];    [delegate cleanUpProgressForTask:task];    [selfremoveNotificationObserverForTask:task];    [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];    [self.lock unlock];}

- (NSArray*)tasksForKeyPath:(NSString*)keyPath {    __blockNSArray*tasks =nil;    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);    [self.session getTasksWithCompletionHandler:^(NSArray*dataTasks,NSArray*uploadTasks,NSArray*downloadTasks) {if([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {            tasks = dataTasks;        }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {            tasks = uploadTasks;        }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {            tasks = downloadTasks;        }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {            tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];        }        dispatch_semaphore_signal(semaphore);    }];    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);returntasks;}

getTasksWithCompletionHandler 这个方法是异步方法,上边的方法中我们需要等待这个异步方法有结果后才能进行后边的代码。 我们就可以使用dispatch_semaphore_t 这个信号来实现异步等待。

具体过程如下:

新建一个信号

在异步方法中发送信号,也就说一旦我们得到了异步的结果,我们就发一个信号

等待信号,只有接收到指定的信号代码才会往下走

这个信号的使用场景有很多,可以当安全锁来使用,也可以像上边一样异步等待。 假如我们有这样一个场景:我们有3个或者多个异步的网络请求,必须等待所有的请求回来后,在使用这些请求的结果来做一些事情。那么该怎么办呢? 解决方案就是:使用dispatch_group_t 和 dispatch_semaphore_t来实现。 在这里代码就不贴出来了,有兴趣的朋友而已自己google或者留言。

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

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

@distinctUnionOfObjects 清楚重复值

unionOfObjects 保留重复值

--

- (NSArray*)tasks {return[selftasksForKeyPath:NSStringFromSelector(_cmd)];}- (NSArray*)dataTasks {return[selftasksForKeyPath:NSStringFromSelector(_cmd)];}- (NSArray*)uploadTasks {return[selftasksForKeyPath:NSStringFromSelector(_cmd)];}- (NSArray*)downloadTasks {return[selftasksForKeyPath:NSStringFromSelector(_cmd)];}

在oc中,当方法被编译器转换成objc_msgSend函数后,除了方法必须的参数,objc_msgSend还会接收两个特殊的参数:receiver 与 selector。

objc_msgSend(receiver, selector, arg1, arg2, ...)

receiver 表示当前方法调用的类实例对象。

selector则表示当前方法所对应的selector。

这两个参数是编译器自动填充的,我们在调用方法时,不必在源代码中显示传入,因此可以被看做是“隐式参数”。

如果想要在source code中获取这两个参数,则可以用self(当前类实例对象)和_cmd(当前调用方法的selector)来表示。

- (void)viewDidLoad{    [superviewDidLoad];NSLog(@"Current method: %@ %@",[selfclass],NSStringFromSelector(_cmd));}输出结果为:TestingProject[570:11303] Current method: FirstViewController viewDidLoad

- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request                              uploadProgress:(nullablevoid(^)(NSProgress*uploadProgress)) uploadProgressBlock                            downloadProgress:(nullablevoid(^)(NSProgress*downloadProgress)) downloadProgressBlock                            completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler {    __blockNSURLSessionDataTask*dataTask =nil;    url_session_manager_create_task_safely(^{        dataTask = [self.session dataTaskWithRequest:request];    });    [selfaddDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];returndataTask;}

这里大概说下几种比较典型的创建task的方法,其他的方法就不做介绍了,原理大体相同。分为下边两个步骤:

创建task

给task添加Delegate

--

- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request                                        fromFile:(NSURL*)fileURL                                        progress:(void(^)(NSProgress*uploadProgress)) uploadProgressBlock                                completionHandler:(void(^)(NSURLResponse*response,idresponseObject,NSError*error))completionHandler{    __blockNSURLSessionUploadTask*uploadTask =nil;    url_session_manager_create_task_safely(^{        uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];    });// 当uploadtTask创建失败,且允许自动创建,会尝试创建uploadtTaskif(!uploadTask &&self.attemptsToRecreateUploadTasksForBackgroundSessions &&self.session.configuration.identifier) {for(NSUIntegerattempts =0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {            uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];        }    }    [selfaddDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];returnuploadTask;}

--

- (NSProgress*)uploadProgressForTask:(NSURLSessionTask*)task {return[[selfdelegateForTask:task] uploadProgress];}- (NSProgress*)downloadProgressForTask:(NSURLSessionTask*)task {return[[selfdelegateForTask:task] downloadProgress];}

- (NSString*)description {return[NSStringstringWithFormat:@"<%@: %p, session: %@, operationQueue: %@>",NSStringFromClass([selfclass]),self,self.session,self.operationQueue];}

假如我们自己写了一个工具类,我们最好重写description方法。

- (BOOL)respondsToSelector:(SEL)selector {if(selector == @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)) {returnself.taskWillPerformHTTPRedirection !=nil;    }elseif(selector == @selector(URLSession:dataTask:didReceiveResponse:completionHandler:)) {returnself.dataTaskDidReceiveResponse !=nil;    }elseif(selector == @selector(URLSession:dataTask:willCacheResponse:completionHandler:)) {returnself.dataTaskWillCacheResponse !=nil;    }elseif(selector == @selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {returnself.didFinishEventsForBackgroundURLSession !=nil;    }return[[selfclass]instancesRespondToSelector:selector];}

我们也可以使用respondsToSelector这个方法来拦截事件,把系统的事件和自定义的事件进行绑定。

NSURLSessionDelegate

// 这个方法是session收到的最后一条信息,- (void)URLSession:(NSURLSession*)sessiondidBecomeInvalidWithError:(NSError*)error{// 调用blockif(self.sessionDidBecomeInvalid) {self.sessionDidBecomeInvalid(session, error);    }// 发送通知[[NSNotificationCenterdefaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];}

--

- (void)URLSession:(NSURLSession*)sessiondidReceiveChallenge:(NSURLAuthenticationChallenge*)challenge completionHandler:(void(^)(NSURLSessionAuthChallengeDispositiondisposition,NSURLCredential*credential))completionHandler{// 创建默认的处理方式,PerformDefaultHandling方式将忽略credential这个参数NSURLSessionAuthChallengeDispositiondisposition =NSURLSessionAuthChallengePerformDefaultHandling;    __blockNSURLCredential*credential =nil;// 调动自身的处理方法,也就是说我们通过sessionDidReceiveAuthenticationChallenge这个block接收session,challenge 参数,返回一个NSURLSessionAuthChallengeDisposition结果,这个业务使我们自己在这个block中完成。if(self.sessionDidReceiveAuthenticationChallenge) {        disposition =self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);    }// 如果没有实现自定义的验证过程else{// 判断challenge的authenticationMethodif([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {// 使用安全策略来验证if([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {// 如果验证通过,根据serverTrust创建依据credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];if(credential) {// 有的话就返回UseCredentialdisposition =NSURLSessionAuthChallengeUseCredential;                }else{                    disposition =NSURLSessionAuthChallengePerformDefaultHandling;                }            }else{// 验证没通过,返回CancelAuthenticationChallengedisposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;            }        }else{            disposition =NSURLSessionAuthChallengePerformDefaultHandling;        }    }if(completionHandler) {        completionHandler(disposition, credential);    }}

着重对这个方法介绍下。

点击查看苹果官方解释

这个代理方法会在下边两种情况下被调用:

当远程服务器要求客户端提供证书或者Windows NT LAN Manager (NTLM)验证

当session初次和服务器通过SSL或TSL建立连接,客户端需要验证服务端证书链

如果没有实现这个方法,session就会调用delegate的URLSession:task:didReceiveChallenge:completionHandler:方法。

如果challenge.protectionSpace.authenticationMethod 在下边4个中时,才会调用

NSURLAuthenticationMethodNTLM

NSURLAuthenticationMethodNegotiate 是否使用KerberosorNTLM验证

NSURLAuthenticationMethodClientCertificate

NSURLAuthenticationMethodServerTrust

否则调用URLSession:task:didReceiveChallenge:completionHandler:方法。

NSURLSessionTaskDelegate

// 请求改变的时候调用- (void)URLSession:(NSURLSession*)session              task:(NSURLSessionTask*)taskwillPerformHTTPRedirection:(NSHTTPURLResponse*)response        newRequest:(NSURLRequest*)request completionHandler:(void(^)(NSURLRequest*))completionHandler{NSURLRequest*redirectRequest = request;if(self.taskWillPerformHTTPRedirection) {        redirectRequest =self.taskWillPerformHTTPRedirection(session, task, response, request);    }if(completionHandler) {        completionHandler(redirectRequest);    }}// 使用方法同 URLSession: didReceiveChallenge: completionHandler: 差不多- (void)URLSession:(NSURLSession*)session              task:(NSURLSessionTask*)taskdidReceiveChallenge:(NSURLAuthenticationChallenge*)challenge completionHandler:(void(^)(NSURLSessionAuthChallengeDispositiondisposition,NSURLCredential*credential))completionHandler{NSURLSessionAuthChallengeDispositiondisposition =NSURLSessionAuthChallengePerformDefaultHandling;    __blockNSURLCredential*credential =nil;if(self.taskDidReceiveAuthenticationChallenge) {        disposition =self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);    }else{if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {if([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {                disposition =NSURLSessionAuthChallengeUseCredential;                credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];            }else{                disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;            }        }else{            disposition =NSURLSessionAuthChallengePerformDefaultHandling;        }    }if(completionHandler) {        completionHandler(disposition, credential);    }}// 请求需要一个全新的,未打开的数据时调用。特别是请求一个body失败时,可以通过这个方法给一个新的body- (void)URLSession:(NSURLSession*)session              task:(NSURLSessionTask*)task needNewBodyStream:(void(^)(NSInputStream*bodyStream))completionHandler{NSInputStream*inputStream =nil;if(self.taskNeedNewBodyStream) {        inputStream =self.taskNeedNewBodyStream(session, task);    }elseif(task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]){        inputStream = [task.originalRequest.HTTPBodyStreamcopy];    }if(completionHandler) {        completionHandler(inputStream);    }}// 上传数据时候调用- (void)URLSession:(NSURLSession*)session              task:(NSURLSessionTask*)task  didSendBodyData:(int64_t)bytesSent    totalBytesSent:(int64_t)totalBytesSenttotalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{    int64_t totalUnitCount = totalBytesExpectedToSend;if(totalUnitCount ==NSURLSessionTransferSizeUnknown) {NSString*contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];if(contentLength) {            totalUnitCount = (int64_t) [contentLength longLongValue];        }    }if(self.taskDidSendBodyData) {self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);    }}// 完成时调用- (void)URLSession:(NSURLSession*)session              task:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error{    AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:task];// delegate may be nil when completing a task in the backgroundif(delegate) {        [delegate URLSession:session task:task didCompleteWithError:error];        [selfremoveDelegateForTask:task];    }if(self.taskDidComplete) {self.taskDidComplete(session, task, error);    }}

NSURLSessionDataDelegate

// 收到响应时调用- (void)URLSession:(NSURLSession*)session          dataTask:(NSURLSessionDataTask*)dataTaskdidReceiveResponse:(NSURLResponse*)response completionHandler:(void(^)(NSURLSessionResponseDispositiondisposition))completionHandler{NSURLSessionResponseDispositiondisposition =NSURLSessionResponseAllow;if(self.dataTaskDidReceiveResponse) {        disposition =self.dataTaskDidReceiveResponse(session, dataTask, response);    }if(completionHandler) {        completionHandler(disposition);    }}//  当NSURLSessionDataTask变为NSURLSessionDownloadTask调用,之后NSURLSessionDataTask将不再接受消息- (void)URLSession:(NSURLSession*)session          dataTask:(NSURLSessionDataTask*)dataTaskdidBecomeDownloadTask:(NSURLSessionDownloadTask*)downloadTask{    AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:dataTask];if(delegate) {        [selfremoveDelegateForTask:dataTask];// 重新设置代理[selfsetDelegate:delegate forTask:downloadTask];    }if(self.dataTaskDidBecomeDownloadTask) {self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);    }}// 接受数据过程中,调用,只限于NSURLSessionDataTask- (void)URLSession:(NSURLSession*)session          dataTask:(NSURLSessionDataTask*)dataTask    didReceiveData:(NSData*)data{    AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:dataTask];    [delegate URLSession:session dataTask:dataTask didReceiveData:data];if(self.dataTaskDidReceiveData) {self.dataTaskDidReceiveData(session, dataTask, data);    }}// 即将缓存响应时调用- (void)URLSession:(NSURLSession*)session          dataTask:(NSURLSessionDataTask*)dataTask willCacheResponse:(NSCachedURLResponse*)proposedResponse completionHandler:(void(^)(NSCachedURLResponse*cachedResponse))completionHandler{NSCachedURLResponse*cachedResponse = proposedResponse;if(self.dataTaskWillCacheResponse) {        cachedResponse =self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);    }if(completionHandler) {        completionHandler(cachedResponse);    }}// 后台任务完成成后- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession*)session {if(self.didFinishEventsForBackgroundURLSession) {dispatch_async(dispatch_get_main_queue(), ^{self.didFinishEventsForBackgroundURLSession(session);        });    }}

NSURLSessionDownloadDelegate

// 下载完成后调用- (void)URLSession:(NSURLSession*)session      downloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location{    AFURLSessionManagerTaskDelegate *delegate = [selfdelegateForTask:downloadTask];if(self.downloadTaskDidFinishDownloading) {NSURL*fileURL =self.downloadTaskDidFinishDownloading(session, downloadTask, location);if(fileURL) {            delegate.downloadFileURL = fileURL;NSError*error =nil;            [[NSFileManagerdefaultManager] moveItemAtURL:location toURL:fileURL error:&error];if(error) {                [[NSNotificationCenterdefaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];            }return;        }    }if(delegate) {        [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];    }}// 下载中调用- (void)URLSession:(NSURLSession*)session      downloadTask:(NSURLSessionDownloadTask*)downloadTask      didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWrittentotalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{if(self.downloadTaskDidWriteData) {self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);    }}// 回复下载时调用,使用fileOffset实现- (void)URLSession:(NSURLSession*)session      downloadTask:(NSURLSessionDownloadTask*)downloadTask didResumeAtOffset:(int64_t)fileOffsetexpectedTotalBytes:(int64_t)expectedTotalBytes{if(self.downloadTaskDidResume) {self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);    }}

** 好了,这篇文章就到此为之了,到目前位置,AFNetworking已经解读了5篇了,所有的核心类也解释完毕,下一篇文章会是AFHTTPSessionManager这个类了 。我们最终的目标是写一个通用的包含大部分功能的网络框架,这个需要在解读完剩余的类之后再实现。我会演示一个从无到有的网络框架的产生过程。**

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容