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的

AFNetworking源码阅读的更多相关文章

  1. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  2. 【原】AFNetworking源码阅读(五)

    [原]AFNetworking源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中提及到了Multipart Request的构建方法- [AFHTTP ...

  3. 【原】AFNetworking源码阅读(四)

    [原]AFNetworking源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇还遗留了很多问题,包括AFURLSessionManagerTaskDe ...

  4. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  5. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

  6. 【原】AFNetworking源码阅读(一)

    [原]AFNetworking源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 AFNetworking版本:3.0.4 由于我平常并没有经常使用AFNetw ...

  7. 【AFNetworking】AFNetworking源码阅读(一)

    1. 前言 2. iOS Example代码结构 3.AFNetworkActivityIndicatorManager 4. UIRefreshControl+AFNetworking 5. AFN ...

  8. AFNetworking源码简析

    AFNetworking基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对iOS和OS X的网络应用设计,具有模块化的架构和丰富的APIs接口,功能强大并且使用简单,深受苹果应用开发人 ...

  9. 【原】FMDB源码阅读(一)

    [原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...

随机推荐

  1. cassandra高级操作之分页的java实现(有项目具体需求)

    接着上篇博客,我们来谈谈java操作cassandra分页,需要注意的是这个分页与我们平时所做的页面分页是不同的,具体有啥不同,大家耐着性子往下看. 上篇博客讲到了cassandra的分页,相信大家会 ...

  2. 【C语言】gets()和scanf()函数的区别

    scanf函数与gets函数 scanf函数和gets( )函数都可用于输入字符串,但在功能上有区别.若想从键盘上输入字符串"hi hello",则应该使用gets函数. gets ...

  3. 关于EF+MVC5分页查询数据效率问题

    2017-03-31 11:57:41,290 [5] ERROR ErrorMsg - System.Data.Entity.Core.EntityCommandExecutionException ...

  4. java如何停止一个运行的线程?

    关于线程的一点心得 //首先导入需要的包 improt java.util.Timer;import java.io.File;import java.util.TimerTask; //首先需要创建 ...

  5. 老李分享:pep8 python代码规范

    老李分享:pep8 python代码规范 什么是PEPPEP是 Python Enhancement Proposal 的缩写,翻译过来就是 Python增强建议书 . PEP8 译者:本文基于 20 ...

  6. 准备要开始写博客啦~Hello World

    Hello World 新的开始 加油咯 小蚊子~

  7. 实现简单的跨站脚本攻击(XSS)

    我们来通俗的了解一下什么是跨站脚本攻击(XSS):在表单中提交 一段 js代码 ,提交的内容被展示到页面时 ,js会被浏览器解析 打个比方吧,比如我现在写的这篇博客,写完以后我要发表对吧? 发表这个过 ...

  8. 人生第一次hash

    人生的第一次hash交给了模板题. 讲道理,还没有别人快排要快,就比暴力快那么一点... 难道我写的hash就那么菜么? 我想了想,光是处理字符串就O(n*len).. 这是hash的正确写法吗?我都 ...

  9. Java线程详解----借鉴

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  10. Spring Boot 配置文件 – 在坑中实践

    摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢!   『 仓廪实而知礼节,衣食足而知荣辱 - 管仲 』   本文提纲 一.自动配置 二.自定义 ...