建议去看原文 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启动时加载所有类的时候调用,且只会调用一次,所以这就有了使用场景了,当想使用运行时做一些事情的时候,就能够用上这个方法了。
举几个使用这个方法的例子:
UITableView+FDTemplateLayoutCell
下边就看看代码部分:
// 根据两个方法名称交换两个方法,内部实现是先根据函数名获取到对应方法实现// 再调用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];}
这个方法返回一个本类的地址,目的是通过这个字符串来判断请求是不是来源于AFNetworking。AFNetworking在为每个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这个类了 。我们最终的目标是写一个通用的包含大部分功能的网络框架,这个需要在解读完剩余的类之后再实现。我会演示一个从无到有的网络框架的产生过程。**
网友评论