iOS AFNetWorking源码详解(五)

[中文教程]
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看430 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 430|回复: 1
收起左侧

[中文教程] iOS AFNetWorking源码详解(五)

[复制链接]
代码买卖 发表于 2016-7-25 02:42:33 | 显示全部楼层 |阅读模式
快来登录
获取优质的苹果资讯内容
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码
订阅梳理好了的知识点专辑
本帖最后由 代码买卖 于 2016-7-27 22:06 编辑

AFURLRequestSerialization是用来对发出的请求进行一些处理

iOS AFNetWorking源码详解

iOS AFNetWorking源码详解(五) - 敏捷大拇指 - iOS AFNetWorking源码详解


AFPercentEscapedStringFromString方法将string里面的:#[]@!$&’()*+,;=字符替换成%

[Objective-C] 纯文本查看 复制代码
NSString * AFPercentEscapedStringFromString(NSString *string) {
          static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
    static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
        
          // 从可用字符替换删除掉:#[]@!$&'()*+,;=这些字符
    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];

        // FIXME: [url=https://github.com/AFNetworking/AFNetworking/pull/3028]https://github.com/AFNetworking/AFNetworking/pull/3028[/url]
    // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        
          // 声明批量处理的大小为50
    static NSUInteger const batchSize = 50;

    NSUInteger index = 0;
    NSMutableString *escaped = @"".mutableCopy;
        
          // 循环将string里面:#[]@!$&'()*+,;=的字符替换成%
    while (index < string.length) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu"
        NSUInteger length = MIN(string.length - index, batchSize);
#pragma GCC diagnostic pop
        NSRange range = NSMakeRange(index, length);

        // To avoid breaking up character sequences such as [attach]30277[/attach]
        range = [string rangeOfComposedCharacterSequencesForRange:range];

        NSString *substring = [string substringWithRange:range];
        NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [escaped appendString:encoded];

        index += range.length;
    }

        return escaped;
}


在AFQueryStringPair类里面有个- URLEncodedStringValue方法,将请求里面的URL参数转成field=value形式

[Objective-C] 纯文本查看 复制代码
- (NSString *)URLEncodedStringValue {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return AFPercentEscapedStringFromString([self.field description]);
    } else {
        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
    }
}


字典里面是我们查询的key和value,我们通过将字典内容转成AFQueryStringPair对象,调用- URLEncodedStringValue方法,转成key=value,放到mutablePairs数组里,最后用&符拼接起来

[Objective-C] 纯文本查看 复制代码
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}


[Objective-C] 纯文本查看 复制代码
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
        
          // 如果是字典,遍历后返回key[nestedKey]=nestedValue
    if ([value isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = value;
        // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            id nestedValue = dictionary[nestedKey];
            if (nestedValue) {
                [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
            }
        }
    } // 如果是数组,遍历后返回key[]=nestedValue
          else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } // 如果是集合,遍历后返回key=obj
          else if ([value isKindOfClass:[NSSet class]]) {
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } // 其他返回key=value 
          else {
        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
    }

    return mutableQueryStringComponents;
}

假设传入的是,我将key,value放到数组里面,再放到mutableQueryStringComponents里

[Objective-C] 纯文本查看 复制代码
NSSet *afSet = [NSSet setWithObjects:@(1),@(2), nil];
NSDictionary *afDic = @{@"dickey": @{@"nestKey": @"nestValue"},
                        @"arrayKey": @[@[@(1)]],
                        @"setKey": afSet,
                        @"generalKey": @"generalValue"};
NSArray *resultArray = AFQueryStringPairsFromKeyAndValue(nil, afDic);


打印得到的结果是

[Swift] 纯文本查看 复制代码
[[arrayKey, 1],
 [dickey[nestKey], nestValue],
 [generalKey, generalValue],
 [setKey, 1],
 [setKey,2]]


我们使用AFHTTPRequestSerializer对HTTP请求的头部进行处理

首先调用+ serializer进行初始化,里面调用了自己init方法

[Objective-C] 纯文本查看 复制代码
+ (instancetype)serializer {
    return [[self alloc] init];
}


init里面先将Accept-Language存到mutableHTTPRequestHeaders里

[Objective-C] 纯文本查看 复制代码
// 将mainBundle里面根据使用语言的优先顺序放到acceptLanguagesComponents里面,再用","分隔,存到mutableHTTPRequestHeaders字典里面
          NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
    [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        float q = 1.0f - (idx * 0.1f);
        [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
        *stop = q <= 0.5f;
    }];
    [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHTTPHeaderField:@"Accept-Language"];


然后拼接User-Agent,格式为”%@/%@ (%@; iOS %@; Scale/%0.2f)”,里面需要5个参数,第一个参数先获取项目名,如果没有,就用BundleIdentifier,第二个参数先获取短版本号,如果没有就用版本号,第三个参数是当前设备的类型,第四个参数是当前设备的版本号,第五个参数是屏幕的比例

[Objective-C] 纯文本查看 复制代码
NSString *userAgent = nil;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
#if TARGET_OS_IOS
    userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
#elif TARGET_OS_WATCH
    // ... 
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
    // ...
#endif
#pragma clang diagnostic pop
if (userAgent) {
        if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
            NSMutableString *mutableUserAgent = [userAgent mutableCopy];
            if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                userAgent = mutableUserAgent;
            }
        }
        [self setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}


然后设置属性的监听,这些属性在头文件里面都可以找到,实现文件里面也实现了set方法

[Objective-C] 纯文本查看 复制代码
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
        }
    }


[Objective-C] 纯文本查看 复制代码
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判断是否是新值,如果是的话,就加到mutableObservedChangedKeyPaths里面

[Objective-C] 纯文本查看 复制代码
- (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];
        }
    }
}


设置验证字段

[Objective-C] 纯文本查看 复制代码
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password
{
    NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
    NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
    [self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}


初始化之后,需要调用

[Objective-C] 纯文本查看 复制代码
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    // 断言                                     
    NSParameterAssert(method);
    NSParameterAssert(URLString);
        
    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);
        // 根据url初始化request
    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    // 设置HTTP方法                                   
    mutableRequest.HTTPMethod = method;
        // 根据mutableObservedChangedKeyPaths存储的属性,设置到mutableRequest
    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }
        // 调用- [requestBySerializingRequest:withParameters:error]方法
    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

        return mutableRequest;
}


[Objective-C] 纯文本查看 复制代码
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(id)parameters
                                        error:(NSError *__autoreleasing *)error
{
    // 断言                                      
    NSParameterAssert(request);

    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    // 根据HTTPRequestHeaders来设置mutableRequest的头部字段
    [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) {
                  // 如果设置了queryStringSerialization这个block的话,就需要设置一个自定义的查询语句序列化方法,转成query查询参数
            NSError *serializationError;
            query = self.queryStringSerialization(request, parameters, &serializationError);

            if (serializationError) {
                if (error) {
                    *error = serializationError;
                }

                return nil;
            }
        } else {
                  // 如果没有设置,则调用AFQueryStringFromParameters方法,转成query查询参数
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }
        
    // 将拼接好的query语句放到 mutableRequest.URL或者放到                                    mutableRequest的HTTPBody里
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query) {
            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"];
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}


下面的话,基本都是对多部分数据进行组装




相关内容

iOS AFNetWorking源码详解(一)

iOS AFNetWorking源码详解(二)

iOS AFNetWorking源码详解(三)

iOS AFNetWorking源码详解(四)

iOS AFNetWorking源码详解(五)

iOS AFNetWorking源码详解(六)

AFNetworking 3.0迁移指南 AFNetworking 3.0 Migration Guide




作者:zeeyang

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

本帖被以下淘专辑推荐:

firefighter 发表于 2016-7-31 01:07:21 | 显示全部楼层
看到这一篇时已经好累了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励

相关标签

热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表