码迷,mamicode.com
首页 > Web开发 > 详细

AFNetworking源码阅读

时间:2017-05-13 14:28:06      阅读:234      评论:0      收藏:0      [点我收藏+]

标签:字符   框架   receiving   是你   upload   erro   failure   oca   header   

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 > 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的

 

AFNetworking源码阅读

标签:字符   框架   receiving   是你   upload   erro   failure   oca   header   

原文地址:http://www.cnblogs.com/dj3839/p/6848688.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!