面试题答案
一键面试AFNetworking处理复杂网络场景的实现原理
- 网络切换
- AFNetworking 通过
AFNetworkReachabilityManager
类来监听网络状态变化。它利用系统提供的SCNetworkReachability
API,通过注册回调函数,当网络状态(如从 WiFi 切换到蜂窝网络,或网络连接断开再恢复)发生改变时,AFNetworkReachabilityManager
会收到通知。 - 当网络状态变化时,AFNetworking 会重新评估请求队列中的请求,对于正在进行的请求,会根据配置决定是否取消或继续。例如,如果配置为允许跨网络切换继续请求,且新网络可用,请求可能会继续执行。
- AFNetworking 通过
- 请求超时重连
- AFNetworking 为每个请求设置了超时时间,默认超时时间是 60 秒(可通过
AFURLSessionManager
的requestSerializer
的timeoutInterval
属性进行设置)。 - 当请求超时时,AFNetworking 会调用
NSURLSession
的相关方法取消当前请求。如果配置了重连策略(例如AFHTTPRequestOperation
中有相关设置),则会按照重连策略(如固定次数重连、指数退避重连等)重新发起请求。例如指数退避重连,每次重连的间隔时间会以指数形式增长,以避免短时间内过多无效请求对服务器造成压力。
- AFNetworking 为每个请求设置了超时时间,默认超时时间是 60 秒(可通过
- 数据缓存
- AFNetworking 提供了
AFURLResponseSerialization
相关类来处理数据缓存。AFImageResponseSerializer
用于图片缓存,AFJSONResponseSerializer
等用于其他类型数据缓存。 - 缓存机制基于
NSURLCache
,当请求返回数据时,AFNetworking 会根据请求的 URL 和缓存策略(如NSURLRequestUseProtocolCachePolicy
、NSURLRequestReloadIgnoringLocalCacheData
等)决定是否缓存数据。如果缓存,会将数据存储在NSURLCache
中,下次相同请求到来时,优先从缓存中读取数据,减少网络请求开销。
- AFNetworking 提供了
AFNetworking可优化点
- 网络切换
- 优化点:网络切换时可能存在请求处理不够智能的情况。例如,在某些场景下,即使网络已切换到新的可用网络,但请求可能由于一些内部状态问题未能及时恢复,导致用户体验不佳。
- 原因:
AFNetworkReachabilityManager
虽然能监听网络状态变化,但请求队列和请求状态管理在复杂场景下可能不够完善,没有充分考虑到不同类型请求对网络切换的适应能力。
- 请求超时重连
- 优化点:重连策略相对固定,不能根据不同业务场景灵活调整。例如,对于一些对实时性要求高的业务,固定次数重连和指数退避重连可能无法满足需求,可能需要更复杂的动态重连策略。
- 原因:当前 AFNetworking 的重连策略是基于通用场景设计,没有针对特定业务需求提供足够的灵活性接口。
- 数据缓存
- 优化点:缓存管理不够精细化。例如,缓存数据的有效期管理相对简单,没有考虑到不同数据的重要性和时效性差异。另外,缓存数据的清理策略可能不够高效,可能导致缓存占用过多内存。
- 原因:
NSURLCache
提供的接口相对基础,AFNetworking 在此基础上没有进一步完善更符合业务需求的缓存管理机制。
优化思路及关键代码逻辑
- 网络切换优化
- 优化思路:增强请求队列和请求状态管理,在网络切换时更准确地判断哪些请求可以继续,哪些需要重新发起。可以为请求添加更多属性,如请求优先级、是否可在网络切换时继续等。
- 关键代码逻辑:在
AFURLSessionManager
类中,修改task:didCompleteWithError:
方法,在网络切换后重新评估请求状态。例如:
- (void)task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error && [AFNetworkReachabilityManager sharedManager].isReachable) {
// 判断请求是否可在网络切换后继续
if ([self.requests[task] valueForKey:@"shouldResumeAfterNetworkChange"]) {
// 重新发起请求
NSURLRequest *request = [self.requests[task] copy];
NSURLSessionDataTask *newTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
// 处理新请求的回调
}];
[newTask resume];
}
}
// 原方法其他逻辑
}
- 请求超时重连优化
- 优化思路:提供更灵活的重连策略接口,允许开发者根据业务需求自定义重连逻辑。例如,创建一个协议
AFCustomRetryPolicy
,开发者可以实现该协议来自定义重连策略。 - 关键代码逻辑:在
AFURLSessionManager
类中,添加一个属性用于存储自定义重连策略对象,修改task:didCompleteWithError:
方法来使用自定义重连策略。
- 优化思路:提供更灵活的重连策略接口,允许开发者根据业务需求自定义重连逻辑。例如,创建一个协议
// 定义协议
@protocol AFCustomRetryPolicy <NSObject>
- (BOOL)shouldRetryRequest:(NSURLRequest *)request withError:(NSError *)error retryCount:(NSUInteger)retryCount;
- (NSTimeInterval)retryIntervalForRequest:(NSURLRequest *)request withError:(NSError *)error retryCount:(NSUInteger)retryCount;
@end
// 在AFURLSessionManager类中添加属性
@property (nonatomic, strong) id<AFCustomRetryPolicy> customRetryPolicy;
// 修改task:didCompleteWithError:方法
- (void)task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
if (error && error.code == NSURLErrorTimedOut) {
NSUInteger retryCount = [self.requests[task] valueForKey:@"retryCount"] ?: 0;
if ([self.customRetryPolicy shouldRetryRequest:self.requests[task] withError:error retryCount:retryCount]) {
NSTimeInterval retryInterval = [self.customRetryPolicy retryIntervalForRequest:self.requests[task] withError:error retryCount:retryCount];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(retryInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSURLRequest *request = [self.requests[task] copy];
NSURLSessionDataTask *newTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
// 处理新请求的回调
}];
[newTask resume];
[self.requests[task] setValue:@(retryCount + 1) forKey:@"retryCount"];
});
}
}
// 原方法其他逻辑
}
- 数据缓存优化
- 优化思路:实现更精细化的缓存管理,为缓存数据添加更多元信息,如重要性级别、有效期等。优化缓存清理策略,根据数据的重要性和有效期进行清理。
- 关键代码逻辑:创建一个自定义的缓存类
AFCustomCache
继承自NSURLCache
,重写storeCachedResponse:forRequest:
和cachedResponseForRequest:
方法。
@interface AFCustomCache : NSURLCache
@end
@implementation AFCustomCache
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
// 添加元信息,如有效期
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:cachedResponse.userInfo];
userInfo[@"expiryDate"] = [NSDate dateWithTimeIntervalSinceNow:3600]; // 假设有效期1小时
NSCachedURLResponse *newCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:cachedResponse.data userInfo:userInfo storagePolicy:cachedResponse.storagePolicy];
[super storeCachedResponse:newCachedResponse forRequest:request];
}
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSCachedURLResponse *cachedResponse = [super cachedResponseForRequest:request];
if (cachedResponse) {
NSDate *expiryDate = cachedResponse.userInfo[@"expiryDate"];
if (expiryDate && [expiryDate compare:[NSDate date]] == NSOrderedAscending) {
// 数据已过期,清理缓存
[self removeCachedResponseForRequest:request];
return nil;
}
}
return cachedResponse;
}
@end
然后在项目中使用自定义缓存类:
AFCustomCache *customCache = [[AFCustomCache alloc] initWithMemoryCapacity:10 * 1024 * 1024 diskCapacity:100 * 1024 * 1024 diskPath:nil];
[NSURLCache setSharedURLCache:customCache];