美文网首页
AFNetworking-AFURLSessionManager

AFNetworking-AFURLSessionManager

作者: Code_人生 | 来源:发表于2019-09-28 21:31 被阅读0次

    一、manager初始化

    • manager是类方法,工厂设计模式,不是单例哦
    • 线程:2.0 AF 建立在常驻线程,3.0 在Runloop NSOperationQueue
        NSString *urlStr = @"https://api.douban.com/v2/movie/top250";
        NSDictionary *dic = @{@"start":@(1),
                              @"count":@(5)
                              };
        AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
        [manager GET:urlStr parameters:dic progress:^(NSProgress * _Nonnull downloadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"%@",responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"%@",error);
        }];
    
    • 1、点击manager
    + (instancetype)manager {
        return [[[self class] alloc] initWithBaseURL:nil];
    }
    
    - (instancetype)init {
        return [self initWithBaseURL:nil];
    }
    
    - (instancetype)initWithBaseURL:(NSURL *)url {
        return [self initWithBaseURL:url sessionConfiguration:nil];
    }
    
    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        return [self initWithBaseURL:nil sessionConfiguration:configuration];
    }
    
    • 2.1、调用父类的方法
    • 2.2、给url添加“/”。重定向,再请求一次,浪费带宽;表示目录,信息拼接,路由。SEO(Search Engine Optimization):汉译为搜索引擎优化。
    • 2.3、给requestSerializer、responseSerializer设置默认值
    - (instancetype)initWithBaseURL:(NSURL *)url
               sessionConfiguration:(NSURLSessionConfiguration *)configuration
    {
        //调用父类初始化方法
        self = [super initWithSessionConfiguration:configuration];
        if (!self) {
            return nil;
        }
    
        // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
        /*
         为了确保NSURL +URLWithString:relativeToURL: works可以正确执行,在baseurlpath的最后添加‘/’
         */
        //url有值且没有‘/’,那么在url的末尾添加‘/’
        // 解释1: 防止重定向 -- www.baidu.com 调用2次 浪费带宽
        // 解释2: 表示目录 信息的拼接 路由
        // 解释3: SEO 搜索引擎优化
        if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
            url = [url URLByAppendingPathComponent:@""];
        }
        self.baseURL = url;
        //给requestSerializer、responseSerializer设置默认值
        self.requestSerializer = [AFHTTPRequestSerializer serializer];
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        return self;
    }
    
    • 3.1、初始化一个session
    • 3.2、给manager的属性设置初始值
    - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
        self = [super init];
        if (!self) {
            return nil;
        }
    
        //设置默认的configuration,配置我们的session
        if (!configuration) {
            configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        }
    
        //持有configuration
        self.sessionConfiguration = configuration;
    
        //设置为delegate的操作队列并发的线程数量1,也就是串行队列
        self.operationQueue = [[NSOperationQueue alloc] init];
        self.operationQueue.maxConcurrentOperationCount = 1;
    
        /*
         -如果完成后需要做复杂(耗时)的处理,可以选择异步队列
         -如果完成后直接更新UI,可以选择主队列
         [NSOperationQueue mainQueue]
         */
        
        self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
    
        //默认为json解析
        self.responseSerializer = [AFJSONResponseSerializer serializer];
    
        //设置默认证书 无条件信任证书https认证
        self.securityPolicy = [AFSecurityPolicy defaultPolicy];
    
    #if !TARGET_OS_WATCH
        //网络状态监听
        self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
    #endif
    
        //delegate= value taskid = key
        self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    
        //使用NSLock确保线程安全
        self.lock = [[NSLock alloc] init];
        self.lock.name = AFURLSessionManagerLockName;
    
        //异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有
        //后台任务重新回来初始化session,可能就会有先前的任务
        //https://github.com/AFNetworking/AFNetworking/issues/3499
        [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            for (NSURLSessionDataTask *task in dataTasks) {
                [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
            }
    
            for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
                [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
            }
    
            for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
                [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
            }
        }];
    
        return self;
    }
    

    二、request方法封装

    • request : 请求行 + 请求头 + 请求体
    • 请求头
    • 请求头参数
    - (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
    {
    
        //返回一个task,然后开始网络请求
        NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                            URLString:URLString
                                                           parameters:parameters
                                                       uploadProgress:nil
                                                     downloadProgress:downloadProgress
                                                              success:success
                                                              failure:failure];
    
        //开始网络请求
        [dataTask resume];
    
        return dataTask;
    }
    
    • 点击dataTaskWithHTTPMethod...
    //1.生成request,2.通过request生成task
    - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                           URLString:(NSString *)URLString
                                          parameters:(id)parameters
                                      uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                    downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                             success:(void (^)(NSURLSessionDataTask *, id))success
                                             failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
    {
        NSError *serializationError = nil;
        /*
         1.先调用AFHTTPRequestSerializer的requestWithMethod函数构建request
         2.处理request构建产生的错误 – serializationError
         //relativeToURL表示将URLString拼接到baseURL后面
    
         */
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        if (serializationError) {
            if (failure) {
                //http://fuckingclangwarnings.com/#semantic
                //xcode忽略编译器的警告,diagnostic:诊断的
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                //completionQueue不存在返回dispatch_get_main_queue
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        //此时的request已经将参数拼接在url后面
        __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;
    }
    
    • 点击requestWithMethod...
    - (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                     URLString:(NSString *)URLString
                                    parameters:(id)parameters
                                         error:(NSError *__autoreleasing *)error
    {
        //断言如果nil,直接打印出来
        NSParameterAssert(method);
        NSParameterAssert(URLString);
    
        //我们传进来的是一个字符串,在这里它帮你转成url
        NSURL *url = [NSURL URLWithString:URLString];
    
        NSParameterAssert(url);
    
        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
        //设置请求方式(get、post、put。。。)
        mutableRequest.HTTPMethod = method;
    
        //将request的各种属性遍历,给NSMutableURLRequest自带的属性赋值
        for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
            //给设置过得的属性,添加到request(如:timeout)
            if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
                //通过kvc动态的给mutableRequest添加value
                [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
            }
        }
    
        //将传入的参数进行编码,拼接到url后并返回 coount=5&start=1
        mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
        NSLog(@"request'''''''''%@",mutableRequest);
    
        return mutableRequest;
    }
    
    • 点击requestBySerializingRequest...
    - (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                                   withParameters:(id)parameters
                                            error:(NSError *__autoreleasing *)error
    {
        NSParameterAssert(request);
    
        NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
        //给mutableRequest.headfiled赋值
        /*
          1.请求行(状态行):get,url,http协议1.1
         2.请求头:conttent-type,accept-language
         3.请求体:get/post get参数拼接在url后面 post数据放在body
        
         */
        [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
            if (![request valueForHTTPHeaderField:field]) {
                [mutableRequest setValue:value forHTTPHeaderField:field];
            }
        }];
    
        //将我们传入的字典转成字符串
        NSString *query = nil;
        if (parameters) {
            //自定义的解析方式
            if (self.queryStringSerialization) {
                NSError *serializationError;
                query = self.queryStringSerialization(request, parameters, &serializationError);
    
                if (serializationError) {
                    if (error) {
                        *error = serializationError;
                    }
    
                    return nil;
                }
            } else {
                //默认解析方式,dic-   count=5&start=1
                switch (self.queryStringSerializationStyle) {
                    case AFHTTPRequestQueryStringDefaultStyle:
                        //将parameters传入这个c函数
                        query = AFQueryStringFromParameters(parameters);
                        break;
                }
            }
        }
    
        //count=5&start=1
        NSLog(@"query:%@",query);
        //最后判断该request中是否包含了GET、HEAD、DELETE(都包含在HTTPMethodsEncodingParametersInURI)。因为这几个method的query是拼接到url后面的。而POST、PUT是把query拼接到http body中的。
        if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
            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 = @"";
            }
            //函数会判断request的Content-Type是否设置了,如果没有,就默认设置为application/x-www-form-urlencoded
            //application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式
            if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
                [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
            }
            //设置请求体
            [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
        }
    
        return mutableRequest;
    }
    

    三、task与代理(AFURLSessionManagerTaskDelegate)的关系

    • manager -> session -> task -> delegate -> /weak/ -> manager
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    

    存在了mutableTaskDelegatesKeyedByTaskIdentifier字典中,task.taskIdentifier为key,delegate为value

    四、manager与代理(AFURLSessionManagerTaskDelegate)的关系

    AFURLSessionManagerTaskDelegate类 中 有AFURLSessionManager属性
    @property (nonatomic, weak) AFURLSessionManager *manager;

    相关文章

      网友评论

          本文标题:AFNetworking-AFURLSessionManager

          本文链接:https://www.haomeiwen.com/subject/gszcyctx.html