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
//在初始化处
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 > ) {
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的