• AFNetworking源码阅读


    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:%@
    length:%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:%@
    length:%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的

  • 相关阅读:
    [Cocoa]iOS中的url编码问题
    [Cocoa]深入浅出Cocoa之消息
    [Cocoa]深入浅出iOS之生命周期
    [Cocoa]深入浅出Cocoa之Bonjour网络编程
    一分钟煮鸡蛋好吃又有营养 生活至上,美容至尚!
    教你几个清肠绝招,做个娇嫩无毒美人 生活至上,美容至尚!
    每天一杯简单排除肠道毒素 生活至上,美容至尚!
    防止电脑辐射必看 保护好你的肌肤 生活至上,美容至尚!
    美容去皱法的误区 生活至上,美容至尚!
    10分钟日本瘦腿操 生活至上,美容至尚!
  • 原文地址:https://www.cnblogs.com/dj3839/p/6848688.html
Copyright © 2020-2023  润新知