1 基本说明
记得我刚做iOS的时候,那时候还是ASI和AFN共存,甚至ASI使用比例还多点,一转眼几年过去,ASI基本已经消失了,AFN基本成了iOS项目的标配。我虽然以前也有看过AFN2.x的源码,但是对于AFN3.x的源码一直没有自己阅读。接下来我会对AFN3.x学习并且写博客记录。得益于NSURLSession
的强大功能,ANF3.0放弃了NSURLConnection
这一部分,让代码简化了很多,但是功能却更加丰富。我觉得在学习AFN之前,有必要仔细了解NSURLSession
和https
相关,不然会有很多地方迷惑不解,具体可以看我的git仓库iOSSourceCodeStudy
。同时我强烈推荐浏览一下NSURLSession.h
这个文件。
2 相互关系
我们首先来看一下一个简单的NSURLSession
请求代码:
1 2 3 4 5 6
| 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];
|
从上面我们发现,我们要发送一个网络请求,需要新建一个NSURLSession
,新建一个NSURLSession
又需要一个NSURLSessionConfiguration
,并且还需要一些代理方法。同时我们需要一个NSURLSessionDataTask
。所以说,我们的NSRULSession
网络请求系统包括一个session、一个configuration、一个Task已经Task附带的delegate。
- 一个
NSURLSession
,总共只有一个类,也是最核心的类,他有一个对应的代理NSURLSessionDelegate
。
- 一个
NSURLSessionConfiguration
,总共有三种模式。
- 一个
NSURLSessionTask
。NSURLSessionTask
是抽闲类,对应的代理NSURLSessionTaskDelegate
。我们具体使用的时候,会使用他的三种子类,而且每个子类都有对应的delegate。
3 一个NSURLSession
首先我们看一下NSRULSession.h
里面关于NSURLSession
的部分。我们把它分为初始化部分、属性部分、dataTask部分、uploadTask部分、downloadTask部分。也就是说其他很多类都是围绕着下面这几个api衍生的。后面我们会每个部分分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @property (class, readonly, strong) 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;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request; - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData; - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request; - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
|
3.1 Block的NSURLSession的api
我们都知道,NSRULConnection
除了一套使用代理的API,还有一套对应的使用Block的api。NSURLSession
也不列外。使用这一套api就不用实现代理方法。和delegate一样,Block也有dataTask系列、downloadTask系列、uploadTask系列。具体看下面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| - (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{ }
- (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{ }
- (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{ }
|
3.2 Block的NSURLSession使用
用dataTask下载一张图片,然后用imageView显示。
1 2 3 4 5 6 7 8 9 10
| -(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]; }
|
4 一个NSURLSessionConfiguration
首先看一下NSURLSessionConfiguration
部分。从这个名字,我们可以预感到这个是与session的配置相关的,的确也是这样。总共有三种类型的configuratin,另外还有很多属性,比如配置缓存策略的requestCachePolicy
,请求超时的timeoutIntervalForRequest
,添加额外请求头的HTTPAdditionalHeaders
,其他还有很多属性这里就不一一说了,具体看源码:
1 2 3 4 5 6 7 8 9 10
| @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;
|
5 一个NSURLSessionTask
从上面NSURLSession
初始化一个请求的时候,我们发现NSURLSessionTask
并不能直接使用,只能使用他的子类。具体如下:
NSURLSessionTask
抽象类。有对应的代理NSURLSessionTaskDelegate
,而且这个代理继承了NSURLSessionDelegate
代理。
NSURLSessionDataTask
是NSURLSessionTask
的子类。有对应的代理NSURLSessionTaskDelegate
,而且这个代理继承了NSURLSessionTaskDelegate
代理。我们一般网络请求,就用这个类。
NSURLSessionDownloadTask
是NSURLSessionTask
的子类。有对应的代理NSURLSessionDownloadDelegate
,而且这个代理继承了NSURLSessionTaskDelegate
代理。这个主要用于下载大文件等。
NSURLSessionUploadTask
是NSURLSessionDataTask
的子类。有对应的代理及时父类代理NSURLSessionDownloadDelegate
。这个主要用于处理上传请求如上传图片。
从上面我们发现Task和delegate有一套对应的继承关系:
从继承关系上,我们就可以理解在初始化的时候,只通过设置NSURLSession
对象的delegate就可以了。因为根据不同的task,其实就是设置了不同的delegate。这个设计避免了多次设置delegate的情况,同时也根据不同的task实现不同的delegate方法。真是一个很绝妙的设计。
6 代理说明
6.1 NSURLSessionDelegate
接下来我们看看NSURLSession
的delegate对象NSURLSessionDelegate
的方法,当一个session遇到错误、或者需要认证、应用进入后台都会调用下面的代理方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error{
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ }
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
}
|
6.2 NSURLSessionTaskDelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
- (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{
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error{ NSLog(@"数据返回以后,不管有错没错都回调用,如果没错,error及时nil"); }
|
6.3 NSURLSessionDataDelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask{ completionHandler(NSURLSessionResponseAllow); }
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask{
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{
}
|
6.4 NSURLSessionDownloadDelegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
- (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{
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
}
|
7 NSURLSession的综合使用案列
分别用三种不同方式下载一张图片然后在imageView上显示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
| #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]; }
- (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; }
#pragma NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error{
}
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{ }
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
}
#pragma NSURLSessionTaskDelegate
- (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(@""); }
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics {
}
- (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; } }
#pragma NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{ self.data = nil; self.data = [NSMutableData data]; completionHandler(NSURLSessionResponseAllow); }
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask{ }
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask{
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{ [self.data appendData:data]; NSLog(@"具体数据在URLSession:(NSURLSession *)session task:(NSURLSessionTask *)taskdidCompleteWithError:(nullable NSError *)error处理"); }
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{
}
#pragma NSURLSessionDownloadTask
- (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); }
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
} @end
|