AFNetworking3.0部分源码理解<一 AFURLRequestSerialization>

才开始是看AF的源码,挺尴尬的,根据自己的理解记录自己看源码学习到的一些东西,主要是一些常用的,第一次写,主要是为了记录,怕自己以后忘了,有错误欢迎指出

AFURLRequestSerialization

这一模块主要是写的是NSURLRequest,用来创建网络请求的管理器;

  • @protocol
@protocol AFURLRequestSerialization <NSObject, NSSecureCoding, NSCopying>

/**
 Returns a request with the specified parameters encoded into a copy of the original request.

 @param request The original request.
 @param parameters The parameters to be encoded.
 @param error The error that occurred while attempting to encode the request parameters.

 @return A serialized request.
 */
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
                               withParameters:(nullable id)parameters
                                        error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

@end

这个主要是用来将parameters解析成query使用的。

- (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) {
        if (self.queryStringSerialization) {
            NSError *serializationError;
            //用户自己通过block设置query
            query = self.queryStringSerialization(request, parameters, &serializationError);

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

                return nil;
            }
        } else {
            switch (self.queryStringSerializationStyle) {
                case AFHTTPRequestQueryStringDefaultStyle:
                     //设置query的方法
                    query = AFQueryStringFromParameters(parameters);
                    break;
            }
        }
    }

    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 = @"";
        }
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        [mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }

    return mutableRequest;
}

将parameters转换成queryString的方法

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

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

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];

    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];

    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)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } else if ([value isKindOfClass:[NSSet class]]) {
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } else {
        [mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
    }

    return mutableQueryStringComponents;
}

上面第一个方法使用了AFQueryStringPair类和第二个方法,然后第二个方法又使用了第三个方法。那么我们先来看第三个方法。

第三个方法实际上就是对parameters进行了逐层解析,最终解析成key和value,并将其存入到了AFQueryStringPair对象中,返回了一个存有AFQueryStringPair对象的数组。
这个方法 [[AFQueryStringPair alloc] initWithField:key value:value]的作用是将字典中的键值对储存为类的两个属性。
那么回到第一个方法,[pair URLEncodedStringValue]];AFQueryStringPair的这个方法是之前存储的两个属性转换成key=value格式的字符串,变成query的格式。

所以

以上三个方法的作用就是将parameters装换成key=value&key1=value1的query类型字符串

  • @interface

定义了一些属性,对系统一些属性通过KVO进行了观察,当发生改变时,就会将其
存储,在创建urlrequest时将其设置为HTTPHeaderField

property (nonatomic, assign) NSStringEncoding stringEncoding;//编码格式
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;
@property (readonly, nonatomic, strong) NSDictionary <NSString *, NSString *> *HTTPRequestHeaders;//请求头

#pragma mark 系统的URLRequest的一些属性   
@property (nonatomic, assign) BOOL allowsCellularAccess;//是否创建能使用设备蜂窝网络的请求,默认为yes
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;//缓存策略 默认NSURLRequestUseProtocolCachePolicy
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;//是否使用默认的cookie处理,default yes
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;//在接受到早期传输的响应之前,创建的请求是否可以继续传输数据。是否收到响应再继续传输新请求 default no
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;//网络服务类型,default NSURLNetworkServiceTypeDefault
@property (nonatomic, assign) NSTimeInterval timeoutInterval;//请求时间,默认60秒

初始化方法

+ (instancetype)serializer;

//设置请求头key value,如果为空,删除请求头中的现有值
- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
//根据key获取请求头value
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;
//身份验证
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
                                       password:(NSString *)password;
//清除验证
- (void)clearAuthorizationHeader;
//设置querystyle,目前只有一种
- (void)setQueryStringSerializationWithStyle:(XDWHTTPRequestQueryStringSerializationStyle)style;//设置style,只有一种0
//手动设置query
- (void)setQueryStringSerializationWithBlock:(nullable NSString * (^)(NSURLRequest *request, id parameters, NSError * __autoreleasing *error))block;
//获得需要的URLRequest
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(nullable id)parameters
                                     error:(NSError * _Nullable __autoreleasing *)error;
  • 扩展
@interface XDWHTTPRequestSerializer ()

@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;//可变请求头字典
@property (readwrite, nonatomic, strong) dispatch_queue_t requestHeaderModificationQueue;//请求头修改队列
@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;//集合 存储KVO观察的方法

@property (readwrite, nonatomic, assign) XDWHTTPRequestQueryStringSerializationStyle queryStringSerializationStyle;//querystyle
@property (readwrite, nonatomic, copy) XDWQueryStringSerializationBlock queryStringSerialization;//修改query的block

@end
  • @implementation

初始化方法

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    self.stringEncoding = NSUTF8StringEncoding;
    self.mutableHTTPRequestHeaders = [NSMutableDictionary dictionary];
    self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
    self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT);
    self.mutableObservedChangedKeyPaths = [NSMutableSet set];
    
    //Accept-Language HTTP Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
    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 Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
    NSString *userAgent = nil;
    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]];
    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"];
    }
    
    //KVO,观察系统NSMutableRequest属性
    for (NSString *keyPath in XDWHTTPRequestSerializerObservedKeyPaths()) {
        if ([self respondsToSelector:@selector(keyPath)]) {
            [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:XDWHTTPRequestSerializerObserverContext];//设置上线文,更好区分
        }
    }
    
    return self;
}

KVO的例子,手动设置KVO需要实现下面的方法,automaticallyNotifiesObserversForKey
以及willChangeValueForKey:didChangeValueForKey
这里设置KVO是为了,如果更改了KVO相关的参数,就是添加到集合中,在创建request时会添加为request的HeaderField中

#pragma mark -- KVO
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([XDWHTTPRequestSerializerObservedKeyPaths() containsObject:key]) {
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}

- (void)setAllowsCellularAccess:(BOOL)allowsCellularAccess {
    [self willChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
    _allowsCellularAccess = allowsCellularAccess;
    [self didChangeValueForKey:NSStringFromSelector(@selector(allowsCellularAccess))];
}

HTTPHeader 设置

- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field {
    dispatch_barrier_async(self.requestHeaderModificationQueue, ^{
        [self.mutableHTTPRequestHeaders setValue:value forKey:field];
    });
}

- (NSDictionary<NSString *,NSString *> *)HTTPRequestHeaders {
    __block NSDictionary *value = nil;
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
    });
    return value;
}

- (NSString *)valueForHTTPHeaderField:(NSString *)field {
    __block NSString *value = nil;
    dispatch_sync(self.requestHeaderModificationQueue, ^{
        value = [self.HTTPRequestHeaders objectForKey:field];
    });
    return value;
}

此处使用了一个线程队列进行修改,设置时使用barrier,避免出现同时操作的情况出现,保证了线程安全,用sync同步取值,保证了是当前线程取到的数据不会被其他的操作更改。保证了数据的时效性准确性.(在并发队列中执行同步操作,如果牵扯到有异步操作正在操作的对象,会阻塞线程进行等待,等这个操作完成后,即之前的所有与这个对象有关的操作完成后,再进行同步操作中与此对象相关的操作,取消线程阻塞,执行后续的操作)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,015评论 11 349
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,635评论 0 9
  • 不争气的身体 2011-04-19 07:39 阅读(41)评论(7) 我的左脚不疼了,右脚又疼,右脚还没好利索又...
    零星往事阅读 517评论 0 0
  • 个人观点:女人,有个好老公,比什么都重要。 1、和老公可以说任何话,这其中有些话是不能对别人说的,但是可以和老公说...
    ayanmei阅读 1,259评论 0 1