记得我刚作iOS的时候,那时候仍是ASI和AFN共存,甚至ASI使用比例还多点,一转眼几年过去,ASI基本已经消失了,AFN基本成了iOS项目的标配。我虽然之前也有看过AFN2.x的源码,可是对于AFN3.x的源码一直没有本身阅读。接下来我会对AFN3.x学习而且写博客记录。得益于NSURLSession
的强大功能,ANF3.0放弃了NSURLConnection
这一部分,让代码简化了不少,可是功能却更加丰富。我以为在学习AFN以前,有必要仔细了解NSURLSession
和https
相关,否则会有不少地方疑惑不解,具体能够看个人git仓库 NSURLSession *sharedSession; + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue; //属性部分 @property (readonly, retain) NSOperationQueue *delegateQueue; @property (nullable, readonly, retain) id <NSURLSessionDelegate> delegate; @property (readonly, copy) NSURLSessionConfiguration *configuration; //dataTask部分 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request; - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url; //uploadTask部分 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData; - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request; //downloadTask部分 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request; - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
咱们都知道,NSRULConnection
除了一套使用代理的API,还有一套对应的使用Block的api。NSURLSession
也不列外。使用这一套api就不用实现代理方法。和delegate同样,Block也有dataTask系列、downloadTask系列、uploadTask系列。具体看下面:app
//dataTask系列 - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ } - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ } //unloadTast系列 - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ } - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ } //downloadTask系列 - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ } - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ } - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler{ }
用dataTask下载一张图片,而后用imageView显示。ide
-(IBAction)requestBlockTaskTest:(id)sender{ [self clear]; NSURLSession *session = [NSURLSession sharedSession]; NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:bigPic]]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { UIImage *image = [[UIImage alloc]initWithData:data]; self.imageView.image = image; }]; [dataTask resume]; }
首先看一下NSURLSessionConfiguration
部分。从这个名字,咱们能够预感到这个是与session的配置相关的,的确也是这样。总共有三种类型的configuratin,另外还有不少属性,好比配置缓存策略的requestCachePolicy
,请求超时的timeoutIntervalForRequest
,添加额外请求头的HTTPAdditionalHeaders
,其余还有不少属性这里就不一一说了,具体看源码:学习
//默认的配置会将缓存存储在磁盘上 @property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration; //第二种瞬时会话模式不会建立持久性存储的缓存. @property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration; //第三种后台会话模式容许程序在后台进行上传下载工做 + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier; //各类属性 @property NSURLRequestCachePolicy requestCachePolicy; @property NSTimeInterval timeoutIntervalForRequest; @property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;
从上面NSURLSession
初始化一个请求的时候,咱们发现NSURLSessionTask
并不能直接使用,只能使用他的子类。具体以下:
NSURLSessionTask
抽象类。有对应的代理NSURLSessionTaskDelegate
,并且这个代理继承了NSURLSessionDelegate
代理。
NSURLSessionDataTask
是NSURLSessionTask
的子类。有对应的代理NSURLSessionTaskDelegate
,并且这个代理继承了NSURLSessionTaskDelegate
代理。咱们通常网络请求,就用这个类。
NSURLSessionDownloadTask
是NSURLSessionTask
的子类。有对应的代理NSURLSessionDownloadDelegate
,并且这个代理继承了NSURLSessionTaskDelegate
代理。这个主要用于下载大文件等。
NSURLSessionUploadTask
是NSURLSessionDataTask
的子类。有对应的代理及时父类代理NSURLSessionDownloadDelegate
。这个主要用于处理上传请求如上传图片。
从上面咱们发现Task和delegate有一套对应的继承关系:
NSURLSessionTask (抽象类,NSURLSessionTaskDelegate
)
NSURLSessionDataTask (NSURLSessionDataDelegate
)
NSURLSessionUploadTask (NSURLSessionDataDelegate
)
NSURLSessionDownloadTask (NSURLSessionDownloadDelegate
)
NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate
从继承关系上,咱们就能够理解在初始化的时候,只经过设置NSURLSession
对象的delegate就能够了。由于根据不一样的task,其实就是设置了不一样的delegate。这个设计避免了屡次设置delegate的状况,同时也根据不一样的task实现不一样的delegate方法。真是一个很绝妙的设计。
接下来咱们看看NSURLSession
的delegate对象NSURLSessionDelegate
的方法,当一个session遇到错误、或者须要认证、应用进入后台都会调用下面的代理方法:
//当一个session遇到系统错误或者未检测到的错误的时候,就会调用这个方法。 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error{ } //当请求须要认证、或者https证书认证的时候,咱们就须要在这个方法里面处理。 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ } //若是应用进入后台、这个方法会被调用。咱们在这里能够对session发起的请求作各类操做好比请求完成的回调等。 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { }
/* 当请求重定向的时候调用这个方法。咱们必须设置一个新的`NSURLRequest`对象传入completionHandler来重定向新的请求,可是当`session`是background模式的时候,这个方法不会被调用。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler{ } /* 当请求须要认证的时候调用这个方法。若是没有实现这个代理,那么请求认证这个过程不会被调用。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ } /* 若是请求须要一个新的请求体时,这个方法就会被调用。好比认证失败的时候,咱们能够经过这个机会重新认证。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler{ } /* 当咱们上传数据的时候,咱们能够经过这个代理方法获取上传进度。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ } /* 当task的统计信息收集好了之后,调用这个方法。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics { } /* 当一个task出错的时候,会调用这个方法。若是error是nil,也会调用这个方法,表示task完成。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error{ NSLog(@"数据返回之后,无论有错没错都回调用,若是没错,error及时nil"); }
/* 当一个task接收到返回信息。当全部信息都接收完毕之后,completionHandler会被调用。咱们能够在这里取消一个网络请求或者把一个datatask转换为downloadtask。若是没有实现这个代理方法,咱们也能够经过task的response属性获取到对应的数据。background模式的uploadtask不会调用这个方法。 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{ } /* 当一个datatask转换为一个downloadtask之后会调用。 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask{ // 容许处理服务器的响应,才会继续接收服务器返回的数据 completionHandler(NSURLSessionResponseAllow); } /* 暂时忽略,这个是和数据流相关的。无论了 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask{ } /* 当data可使用的时候,调用这个方法。咱们能够在这里获取data。 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{ } /* 容许咱们在这里调用completionHandler缓存data,或者传入nil来禁止缓存 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{ }
/* 当一个下载task任务完成之后,这个方法会被调用。咱们能够在这里移动或者复制download的数据 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ } /* 获取下载进度 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ } /* 重启一个下载任务(好比下载一半后中止而后过一点时间继续)。若是下载出错,`NSURLSessionDownloadTaskResumeData`里面包含从新开始下载的数据。 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ }
分别用三种不一样方式下载一张图片而后在imageView上显示。
#import "ViewController.h" static NSString *const bigPic = @"http://i1.piimg.com/4851/d1498fea89ae3bc1.png"; static NSString *const smallPic = @"http://i1.piimg.com/4851/97aef4680d359905.png"; @interface ViewController ()<NSURLSessionDelegate> @property (weak, nonatomic) IBOutlet UIImageView *imageView; @property(nonatomic,strong)NSMutableData *data; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (IBAction)requestDataTest:(id)sender { [self clear]; NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:bigPic]]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request]; [dataTask resume]; } - (IBAction)requestDownloadTest:(id)sender { [self clear]; NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:bigPic]]; NSURLSessionDownloadTask *dataTask = [session downloadTaskWithRequest:request]; [dataTask resume]; } -(IBAction)requestBlockTaskTest:(id)sender{ [self clear]; NSURLSession *session = [NSURLSession sharedSession]; NSURLRequest *request = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:bigPic]]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { UIImage *image = [[UIImage alloc]initWithData:data]; self.imageView.image = image; }]; [dataTask resume]; } -(void)clear{ self.imageView.image = nil; } //==============================NSURLSessionDelegate======================== #pragma NSURLSessionDelegate //当一个session遇到系统错误或者未检测到的错误的时候,就会调用这个方法。 - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error{ } //当请求须要认证、或者https证书认证的时候,咱们就须要在这个方法里面处理。 - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ } //若是应用进入后台、这个方法会被调用。咱们在这里能够对session发起的请求作各类操做好比请求完成的回调等。 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { } //==================================NSURLSessionTaskDelegate==================== #pragma NSURLSessionTaskDelegate /* 当请求重定向的时候调用这个方法。咱们必须设置一个新的`NSURLRequest`对象传入completionHandler来重定向新的请求,可是当`session`是background模式的时候,这个方法不会被调用。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler{ } /* 当请求须要认证的时候调用这个方法。若是没有实现这个代理,那么请求认证这个过程不会被调用。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ } /* 若是请求须要一个新的请求体时,这个方法就会被调用。好比认证失败的时候,咱们能够经过这个机会重新认证。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler{ } /* 当咱们上传数据的时候,咱们能够经过这个代理方法获取上传进度。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{ NSLog(@""); } /* 当task的统计信息收集好了之后,调用这个方法。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics { } /* 当一个task出错的时候,会调用这个方法。若是error是nil,也会调用这个方法,表示task完成。 */ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error{ NSLog(@"数据返回之后,无论有错没错都回调用,若是没错,error及时nil"); if (self.data) { self.imageView.image = [UIImage imageWithData:self.data]; self.data = nil; } } //==================================NSURLSessionDataDelegate===================================== #pragma NSURLSessionDataDelegate /* 当一个task接收到返回信息。当全部信息都接收完毕之后,completionHandler会被调用。咱们能够在这里取消一个网络请求或者把一个datatask转换为downloadtask。若是没有实现这个代理方法,咱们也能够经过task的response属性获取到对应的数据。background模式的uploadtask不会调用这个方法。 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{ self.data = nil; self.data = [NSMutableData data]; // 容许处理服务器的响应,才会继续接收服务器返回的数据 completionHandler(NSURLSessionResponseAllow); } /* 当一个datatask转换为一个downloadtask之后会调用。 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask{ } /* 暂时忽略,这个是和数据流相关的。无论了 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask{ } /* 当data可使用的时候,调用这个方法。咱们能够在这里获取data。 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{ [self.data appendData:data]; NSLog(@"具体数据在URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskdidCompleteWithError:(nullable NSError *)error处理"); } /* 容许咱们在这里调用completionHandler缓存data,或者传入nil来禁止缓存 */ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{ } //==================================NSURLSessionDownloadTask================================= #pragma NSURLSessionDownloadTask /* 当一个下载task任务完成之后,这个方法会被调用。咱们能够在这里移动或者复制download的数据 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSString *path = location.absoluteString; UIImage *image = [[UIImage alloc]initWithData:[NSData dataWithContentsOfURL:location]]; self.imageView.image = image; NSLog(@"数据下载完成之后,会保存在一个location的地方。%@",location); } /* 获取下载进度 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ NSLog(@"总得数据大小%lld----",bytesWritten); } /* 重启一个下载任务(好比下载一半后中止而后过一点时间继续)。若是下载出错,`NSURLSessionDownloadTaskResumeData`里面包含从新开始下载的数据。 */ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{ } @end