原始粗狂版本
CTNetworking 源码分析
从最外部的业务层开始,一步一步的向底层分析
demo 中FireSingleAPI 类作为业务层网络请求的入口
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
//开始网络请求
[self.testAPIManager loadData];
}
//进行网络请求的类
- (TestAPIManager *)testAPIManager
{
if (_testAPIManager == nil) {
_testAPIManager = [[TestAPIManager alloc] init];
//网络请求结果回调
_testAPIManager.delegate = self;
//网络请求参数代理
_testAPIManager.paramSource = self;
}
return _testAPIManager;
}
//设置网络请求参数的代理
pragma mark - CTAPIManagerParamSource
-
(NSDictionary *)paramsForApi:(CTAPIBaseManager *)manager
{
NSDictionary *params = @{};if (manager == self.testAPIManager) {
params = @{
kTestAPIManagerParamsKeyLatitude:@(31.228000),
kTestAPIManagerParamsKeyLongitude:@(121.454290)
};
}
return params;
}
//网络请求结果代理
pragma mark - CTAPIManagerCallBackDelegate
//请求成功
- (void)managerCallAPIDidSuccess:(CTAPIBaseManager *)manager
{
if (manager == self.testAPIManager) {
self.resultLable.text = @"success";
NSLog(@"%@", [manager fetchDataWithReformer:nil]);
[self layoutResultLable];
}
}
//请求失败
- (void)managerCallAPIDidFailed:(CTAPIBaseManager *)manager
{
if (manager == self.testAPIManager) {
self.resultLable.text = @"fail";
NSLog(@"%@", [manager fetchDataWithReformer:nil]);
[self layoutResultLable];
}
}
以上就是业务层关于CTNetworking 网络框架的用法。
使用 TestAPIManager 进行网络请求, 设置请求参数, 获取请求结果
TestAPIManager 类
这里详细看一下 TestAPIManager 是如何进行网络请求, 设置请求参数 处理请求结果的
从loadData 开始
- (NSInteger)loadData
{
//获取请求参数, self.paramSource 的代理一般都是进行网络请求的业务层类 上面我们已经设置了请求参数,这里直接获取
NSDictionary *params = [self.paramSource paramsForApi:self];
//进行网络请求
NSInteger requestId = [self loadDataWithParams:params];
return requestId;
}
查看loadDataWithParams 方法
这个方法,详细处理了网络请求的具体操作
- 网络请求是否会被拦截,这里出现了网络拦截器 的类 CTAPIManagerInterceptor
使用网络拦截器来 判断网络请求参数是否合法,合法则继续网络请求,非法则拦截网络请求 - 查看是否有缓存,如果有缓存 则不进行网络请求直接 取本地缓存的数据
- 没有本地缓存则从服务器请求数据
-
(NSInteger)loadDataWithParams:(NSDictionary *)params
{NSInteger requestId = 0;
NSDictionary *apiParams = [self reformParams:params];
if ([self shouldCallAPIWithParams:apiParams]) {
//判断请求参数是否合法
if ([self.validator manager:self isCorrectWithParamsData:apiParams]) {
// shouldLoadFromNative 在TestAPIManager中并没有实现, 而是在TestAPIManager的父类CTAPIBaseManager 中实现了该方法,其实当 程序走到这里是 self就是TestAPIManager,
//所以把shouldLoadFromNative 实现在TestAPIManager 类中和实现在CTAPIBaseManager类中原理是一样的,此时TestAPIManager 就是CTAPIBaseManager 类
if ([self.child shouldLoadFromNative]) {//是否加载 本地缓存数据
[self loadDataFromNative];
}// 先检查一下是否从本笃获取缓存数据 if ([self shouldCache] && [self hasCacheWithParams:apiParams]) { return 0; } // 从服务器获取数据 if ([self isReachable]) {//检查是否联网,网络状态 self.isLoading = YES;//开启加载状态 switch (self.child.requestType) { //GET case CTAPIManagerRequestTypeGet: AXCallAPI(GET, requestId); break; //POST case CTAPIManagerRequestTypePost: AXCallAPI(POST, requestId); break; //PUT case CTAPIManagerRequestTypePut: AXCallAPI(PUT, requestId); break; //DELETE case CTAPIManagerRequestTypeDelete: AXCallAPI(DELETE, requestId); break; default: break; } NSMutableDictionary *params = [apiParams mutableCopy]; params[kCTAPIBaseManagerRequestID] = @(requestId); [self afterCallingAPIWithParams:params]; return requestId; } else { [self failedOnCallingAPI:nil withErrorType:CTAPIManagerErrorTypeNoNetWork]; return requestId; } } else { [self failedOnCallingAPI:nil withErrorType:CTAPIManagerErrorTypeParamsError]; return requestId; }
}
return requestId;
}
我们先从服务器请求数据开始,加载本地缓存 后面再分析
当网络请求成功之后,会调用方法:successedOnCallingAPI
主要处理事项:
- 验证网络数据是否合法
- 将网络数据缓存到本地 用CTCachedObject 类去缓存数据
- 响应成功 通知代理(业务层)去将网络数据更新到UI
- 响应的数据出错 通知代理(业务层) 让UI做失败处理
-
(void)successedOnCallingAPI:(CTURLResponse *)response
{
//网络等待状态
self.isLoading = NO;
self.response = response;
//是否从本地加载
if ([self.child shouldLoadFromNative]) {
if (response.isCache == NO) {
//将响应数据 保存到本地
[[NSUserDefaults standardUserDefaults] setObject:response.responseData forKey:[self.child methodName]];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
//将响应数据 赋值给 self
if (response.content) {
self.fetchedRawData = [response.content copy];
} else {
self.fetchedRawData = [response.responseData copy];
}
//把 response 从响应队列中删除
[self removeRequestIdWithRequestID:response.requestId];
//验证返回 数据的参数是否正确
if ([self.validator manager:self isCorrectWithCallBackData:response.content]) {//正确
//缓存 请求到的数据
if ([self shouldCache] && !response.isCache) {
[self.cache saveCacheWithData:response.responseData serviceIdentifier:self.child.serviceType methodName:self.child.methodName requestParams:response.requestParams];
}if ([self beforePerformSuccessWithResponse:response]) { if ([self.child shouldLoadFromNative]) {//是否从加载本地缓存 if (response.isCache == YES) {//缓存的response //业务层 处理响应数据 [self.delegate managerCallAPIDidSuccess:self]; } if (self.isNativeDataEmpty) {//本地缓存为空 //业务层 处理响应数据 [self.delegate managerCallAPIDidSuccess:self]; } } else { //业务层 处理响应数据 [self.delegate managerCallAPIDidSuccess:self]; } } //获取到响应数据之后的处理 [self afterPerformSuccessWithResponse:response];
} else {
//响应数据 不正确
[self failedOnCallingAPI:response withErrorType:CTAPIManagerErrorTypeNoContent];
//CTAPIManagerErrorTypeNoContent API请求成功但返回数据不正确。如果回调数据验证函数返回值为NO,manager的状态就会是这个
}
}
上面的方法是响应数据成功后的处理
现在分享响应数据失败之后的处理
方法:failedOnCallingAPI
主要处理事项:
根据不同的错误类型 做不同的处理
1.token 过期
2.token 无效
3.无权限
4.其他错误
方法如下:
//针对不同响应数据失败类型 分别处理
- (void)failedOnCallingAPI:(CTURLResponse *)response withErrorType:(CTAPIManagerErrorType)errorType
{
self.isLoading = NO;//加载状态为 NO
self.response = response;
if ([response.content[@"id"] isEqualToString:@"expired_access_token"]) {
// token 过期
//发送 token 过期通知
[[NSNotificationCenter defaultCenter] postNotificationName:kBSUserTokenInvalidNotification
object:nil
userInfo:@{
kBSUserTokenNotificationUserInfoKeyRequestToContinue:[response.request mutableCopy],
kBSUserTokenNotificationUserInfoKeyManagerToContinue:self
}];
} else if ([response.content[@"id"] isEqualToString:@"illegal_access_token"]) {
// token 无效,重新登录
[[NSNotificationCenter defaultCenter] postNotificationName:kBSUserTokenIllegalNotification
object:nil
userInfo:@{
kBSUserTokenNotificationUserInfoKeyRequestToContinue:[response.request mutableCopy],
kBSUserTokenNotificationUserInfoKeyManagerToContinue:self
}];
} else if ([response.content[@"id"] isEqualToString:@"no_permission_for_this_api"]) {
//非法权限
[[NSNotificationCenter defaultCenter] postNotificationName:kBSUserTokenIllegalNotification
object:nil
userInfo:@{
kBSUserTokenNotificationUserInfoKeyRequestToContinue:[response.request mutableCopy],
kBSUserTokenNotificationUserInfoKeyManagerToContinue:self
}];
} else {
// 其他错误
self.errorType = errorType;
//在响应列表中删除 该响应事件
[self removeRequestIdWithRequestID:response.requestId];
if ([self beforePerformFailWithResponse:response]) {
// 业务层回调 请求响应数据失败方法
[self.delegate managerCallAPIDidFailed:self];
}
[self afterPerformFailWithResponse:response];
}
}
CTAPIBaseManager 类就分析到这
比较两个方法是否相等:
如果遇到两个同名的方法,怎么样才能判断他们是同一个方法呢?
如下:
IMP childIMP = [self.child methodForSelector:@selector(reformParams:)];//代理方法
IMP selfIMP = [self methodForSelector:@selector(reformParams:)];//自身方法
if (childIMP == selfIMP) {//判断是否是同一个方法
return params;
}
如何处理缓存。
创建缓存对象CTCachedObject ,对象的属性有 缓存内容,缓存是否为空,缓存是否过期,缓存跟新时间
方法有:带有缓存内容的初始化方法, 更新缓存内容的方法
缓存的保存,获取,删除等操作, 把这些操作抽象成一个对象 CTCache 。
考虑到 缓存到处都在使用,把他设为单类。
他有一个 NSCache 对象,可以对 缓存对象CTCachedObject进行增删改的操作。