iOS AFNetWorking源码详解(五)

[中文教程]
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看524 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 524|回复: 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.