get方法:
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters progress:(void (^)(NSProgress * _Nonnull))downloadProgress success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure;
方法名:GET
URLString: 请求的url地址,必须加上协议(如:http://localhost:8080/ios)
parameters: url中的参数,以NSDictionNarry的方式存储
progress: 在下载的过程中,会持续调用这个block,这个block是在session queue中被调用的,而不是在main queue中。
success: 下载成功后,调用这个block
failure: 下载失败后,调用这个block
先了解下foundation框架中的网络请求NSURLSessionDataTask的最基本使用:
//get请求 - (void)getRequest { //得到session对象 NSURLSession *session = [NSURLSession sharedSession]; NSURL *url = [NSURL URLWithString:@"http://10.81.160.87:8080/ios?name=dj&sex=male"]; //创建一个任务 NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error: %@",error); } else { // 需要对data进行转码 NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); NSLog(@"data:%@\nlength:%ld", data, data.length); } }]; //开始任务 [task resume]; } //post请求 - (void)postRequest { //得到session对象 NSURLSession *session = [NSURLSession sharedSession]; //设置url地址 NSURL *url = [NSURL URLWithString:@"http://10.81.160.87:8080/ios"]; //设置request请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; //设置request请求的方法 request.HTTPMethod = @"post"; //设置request请求的参数 request.HTTPBody = [@"name=dj&&sex=male" dataUsingEncoding:NSUTF8StringEncoding]; //创建一个任务 NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error: %@",error); } else { // 需要对data进行转码 NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); NSLog(@"data:%@\nlength:%ld", data, data.length); } }]; //开始工作 [task resume]; }
通过协议的实现,我们可以实现更多的功能,如下载上传时的状态
知道了NSURLSessionDataTask的基本应用,接着看
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:downloadProgress success:success failure:failure];
这个方法得到一个NSURLSessionDataTask对象dataTask,如何得到,进入dataTaskWithHTTPMethod方法
首先是一个得到NSMutableURLRequest的方法
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error { NSParameterAssert(method); //不存在就抛出异常,断言 NSParameterAssert(URLString); NSURL *url = [NSURL URLWithString:URLString]; //将url字符串转为url NSParameterAssert(url); NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url]; //初始化一个NSMutableURLRequest mutableRequest.HTTPMethod = method; //设置请求方式 for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; } } mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]; return mutableRequest; }
在上面有个AFHTTPRequestSerializerObservedKeyPaths方法,这个方法返回的是一个字符串数组,而且是一个单例对象
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() { static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))]; }); return _AFHTTPRequestSerializerObservedKeyPaths; }
这里的设置其实用到了Kvo
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #4f8187 }
p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #31595d }
p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #3e1e81 }
p.p4 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000 }
span.s1 { color: #ba2da2 }
span.s2 { color: #000000 }
span.s3 { }
span.s4 { color: #703daa }
span.s5 { color: #3e1e81 }
span.s6 { color: #4f8187 }
//在初始化处
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(__unused id)object change:(NSDictionary *)change context:(void *)context { if (context == AFHTTPRequestSerializerObserverContext) { if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) { [self.mutableObservedChangedKeyPaths removeObject:keyPath]; } else { [self.mutableObservedChangedKeyPaths addObject:keyPath]; } } }
当keyPath发生改变,mutableObservedChangedKeyPaths这个set容器就添加一个keyPath
在刚刚[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];中,就是将发生改变的keyPath加入到mutableRequest中。
那keyPath是什么呢?
我们其实可以先看下,mutableRequest里面都有哪些属性可以设置
@property (nullable, copy) NSURL *URL; @property NSURLRequestCachePolicy cachePolicy; @property NSTimeInterval timeoutInterval; @property (nullable, copy) NSURL *mainDocumentURL; @property NSURLRequestNetworkServiceType networkServiceType NS_AVAILABLE(10_7, 4_0); @property BOOL allowsCellularAccess NS_AVAILABLE(10_8, 6_0);
然后我们再看看requestSerializer这个对象,因为我们刚刚requestWithMethod方法就是它的实例方法,我们在看看这个requestWithMethod这个方法中的属性,
这里直接复制源码,刚好可以解释mutableRequest中各个属性的作用和初始值
/** The string encoding used to serialize parameters. `NSUTF8StringEncoding` by default. */ @property (nonatomic, assign) NSStringEncoding stringEncoding; /** Whether created requests can use the device’s cellular radio (if present). `YES` by default. @see NSMutableURLRequest -setAllowsCellularAccess: */ @property (nonatomic, assign) BOOL allowsCellularAccess; /** The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default. @see NSMutableURLRequest -setCachePolicy: */ @property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy; /** Whether created requests should use the default cookie handling. `YES` by default. @see NSMutableURLRequest -setHTTPShouldHandleCookies: */ @property (nonatomic, assign) BOOL HTTPShouldHandleCookies; /** Whether created requests can continue transmitting data before receiving a response from an earlier transmission. `NO` by default @see NSMutableURLRequest -setHTTPShouldUsePipelining: */ @property (nonatomic, assign) BOOL HTTPShouldUsePipelining; /** The network service type for created requests. `NSURLNetworkServiceTypeDefault` by default. @see NSMutableURLRequest -setNetworkServiceType: */ @property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType; /** The timeout interval, in seconds, for created requests. The default timeout interval is 60 seconds. @see NSMutableURLRequest -setTimeoutInterval: */ @property (nonatomic, assign) NSTimeInterval timeoutInterval;
现在就清晰了,我们通过设置requestSerializer这个对象的属性,kvo检测到属性的变化,将变化的属性放入mutableObservedChangedKeyPaths这个set容器中,
然后在将set容器中的值,设置到mutableRequest中,这样我们就改变了mutableRequest的属性
接下来还有一个方法:
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy]
作用是将参数加到mutableRequest中,分析下它的实现
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error { NSParameterAssert(request); NSMutableURLRequest *mutableRequest = [request mutableCopy]; //设置头请求 [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }]; NSString *query = nil; //有参数 if (parameters) { //如果你实现了下面queryStringSerialization这个块,那你的query就是你自己实现的 if (self.queryStringSerialization) { NSError *serializationError; query = self.queryStringSerialization(request, parameters, &serializationError); //如有出错,返回nil if (serializationError) { if (error) { *error = serializationError; } return nil; } } else { //这是个枚举类型的属性,默认为AFHTTPRequestQueryStringDefaultStyle //AFQueryStringFromParameters将字典转为带&的字符串 switch (self.queryStringSerializationStyle) { case AFHTTPRequestQueryStringDefaultStyle: query = AFQueryStringFromParameters(parameters); break; } } } //HTTPMethodsEncodingParametersInURI是个set容器,初始数据有get,head,deal //判断是否是get请求 if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { //封装url if (query && query.length > 0) { mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]]; } } else { // #2864: an empty string is a valid x-www-form-urlencoded payload if (!query) { query = @""; } //添加头请求 if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; } //设置body参数 [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]]; } return mutableRequest; }
在上面设置头请求的时候,其实要知道,它是通过mutableHTTPRequestHeaders得到的
- (NSDictionary *)HTTPRequestHeaders { return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; }
而这个mutableHTTPRequestHeaders需要通过下面这个方法进行设置==
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { [self.mutableHTTPRequestHeaders setValue:value forKey:field]; }
知道了参数的设置,就这样我们得到了这个NSMutableURLRequest对象
然后判断是否有错误,若有,则抛出错误,并返回nil
接下来执行
__block NSURLSessionDataTask *dataTask = nil; dataTask = [self dataTaskWithRequest:request uploadProgress:uploadProgress downloadProgress:downloadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { if (error) { if (failure) { failure(dataTask, error); } } else { if (success) { success(dataTask, responseObject); } } }]; return dataTask;
dataTaskWithRequest方法得到一个NSURLSessionDataTask对象,并返回这个对象
看下实现
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler { __block NSURLSessionDataTask *dataTask = nil; //这是个静态方法 url_session_manager_create_task_safely(^{ dataTask = [self.session dataTaskWithRequest:request]; }); [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; return dataTask; }
看下静态方法url_session_manager_create_task_safely的实现
static void url_session_manager_create_task_safely(dispatch_block_t block) { if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) { // Fix of bug // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8) // Issue about:https://github.com/AFNetworking/AFNetworking/issues/2093 //同步执行串行队列 dispatch_sync(url_session_manager_creation_queue(), block); } else { block(); } }
url_session_manager_creation_queue()静态方法的实现
//单例模式,返回一个串行队列static dispatch_queue_t url_session_manager_creation_queue() { static dispatch_queue_t af_url_session_manager_creation_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); }); return af_url_session_manager_creation_queue; }
AFURLSessionManagerTaskDelegate这个类遵从了NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate三个协议。
- (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 { AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; delegate.manager = self; delegate.completionHandler = completionHandler; dataTask.taskDescription = self.taskDescriptionForSessionTasks; [self setDelegate:delegate forTask:dataTask]; delegate.uploadProgressBlock = uploadProgressBlock; delegate.downloadProgressBlock = downloadProgressBlock; }
通过上面的addDelegateForDataTask方法,AFURLSessionManagerTaskDelegate得到了各个属性,当在NSURLSessionDataTask开始工作时,会通过协议的实现调用这些属性块。
到这里,就可以通过[dataTask resume];来开始http的get请求了
其实分析下来可以发现,无论是get请求,还是post请求,无论下载,还是上传,都是
[self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters uploadProgress:nil downloadProgress:downloadProgress success:success failure:failure]; [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
其实就改变了method参数,当然上传通过post,下载通过get
AFNetworking的源码分析当然远远不止这些,我只是记录下我暂时使用到方法在框架中是如何实现,如何封装NSURLSessionDataTask的