iOS高级开发工程师面试——常见第三方框架架构设计
一、SDWebImage
1. SDWebImage 组织架构:
2. SDWebImage 图片加载流程:
二、AFNetWorking
AFNetWorking 组织架构:主要有5个模块
一、SDWebImage
1. SDWebImage 组织架构:
![图片[1] - iOS高级开发工程师面试——常见第三方框架架构设计 - 宋马](https://pic.songma.com/blogimg/20250725/b26b9369c2b34e64962330e36e4f54f1.png)
SDWebImageDownloader :负责维持图片的下载队列;
SDWebImageDownloaderOperation:负责真正的图片下载请求;
SDImageCache:负责图片的缓存;
SDWebImageManager:是总的管理类,维护了一个SDWebImageDownloader 实例和一个 SDImageCache 实例,是下载与缓存的桥梁;
SDWebImageDecoder:负责图片的解压缩;
SDWebImagePrefetcher:负责图片的预取;
UIImageView+WebCache:和其他的扩展都是与用户直接打交道的。
2. SDWebImage 图片加载流程:

判断图片URL 是否为 nil,是则做出错处理并返回;
URL MD5加密生成 key;
根据 key 读取内存(memory)缓存, 有则拿到图片返回,否则往下;
根据 key 读取磁盘(disk)缓存,有则拿到图片返回,否则往下;
根据URL 下载图片,下载成功则将图片保存到 内存和磁盘中返回图片
二、AFNetWorking
AFNetWorking 组织架构:主要有5个模块
AFHTTPSessionManager :是对 NSURLSession 的封装,负责发送网络请求,是 AFNetWotking 中使用最多一个模块
AFNetworkingReachabilityManager :实时监测网络状态的工具类
AFSecurityPolicy :网络安全策略的工具类,主要是针对于 Https 服务
Serializstion :请求序列化工具类
AFURLRequestSerialization:请求入参序列化工具基类
AFURLResponseSerialization :请求回参序列化工具基类
AFJSONResponseSerializer : Json 解析器,
AFNetWorking 的默认解析器
AFXMLParserResponseSerializer :XML 解析器
AFHTTPResponseSerializer : 万能解析器,直接返回二进制数据(NSData),服务器不会对数据进行处理
UIKit : 对iOS UIKit 的扩展
5.1. AFNetworking 2.x怎么开启常驻子线程?为何需要常驻子线程?
在 2.x 版本中 AFNetWorking 通过 RunLoop 开启了一个常驻子线程,具体代码是这样的
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *RunLoop = [NSRunLoop currentRunLoop];
[RunLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[RunLoop run];
}
}
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
为何要开启常驻子线程?
NSURLConnection 的接口是异步的,然后会在发起的线程回调。而一个子线程,在同步代码执行完成之后,一般情况下,线程就退出了。那么想要接收到 NSURLConnection 的回调,就必须让子线程至少存活到回调的时机。而AF让线程常驻的原因是,当发起多个http请求的时候,会统一在这个子线程进行回调的处理,所以干脆就让其一直存活下来。
上面说的一般情况,子线程执行完任务就会退出。子线程能够继续存活,就需要通过RunLoop来开启常驻线程。
5.2. AFURLSessionManager 与 NSURLSession 的关系,每次都需要新建 manager 吗?
AFNetWorking 中 manager 与 session 是1对1的关系, AFNetWorking 会在 manager 初始化的时候创建对应的 NSURLSession 。同样, AFNetWorking 也在注释中写明了可以提供一个配置好的 manager 单例来全局复用。
这里复用 session 其实就是在利用 http2.0 的多路复用特点,减少访问同一个服务器时,重新建立 tcp 连接的耗时和资源。
5.3. AFSecurityPolicy 如何避免中间人攻击?
现在,由于苹果ATS的策略,基本都切到HTTPS了,HTTPS 的基本原理还是需要了解一下的,这里不做介绍。
通常,首先我们要了解中间人攻击,大体就是黑客通过截获服务器返回的证书,并伪造成自己的证书,通常我们使用的 Charles/Fiddler 等工具实际上就可以看成中间人攻击。
解决方案其实也很简单,就是 SSL Pinning 。 AFSecurityPolicy 的 AFSSLPinningMode 就是相关设置项。
SSL Pinning 的原理就是需要将服务器的公钥打包到客户端中, tls 验证时,会将服务器的证书和本地的证书做一个对比,一致的话才允许验证通过
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey, // 只验证证书中的公钥
AFSSLPinningModeCertificate, // 验证证书所有字段,包括有效期之内
};
由于数字证书存在有效期,内置到客户端后就存在失效后导致验证失败的问题,所以可以考虑设置为 AFSSLPinningModePublicKey 的模式,这样的话,只要保证证书续期后,证书中的公钥不变,就能够通过验证了。
5.4. AFNetWorking 3.x 为什么不再需要常驻线程?
AFNetWorking 2.x 使用NSURLConnection,痛点就是:**发起请求后,这条线程并不能随风而去,而需要一直处于等待回调的状态。**所以 AFNetWorking2.x 在权衡之后选择了常驻线程。
AFNetWorking 3.x 之后使用了 NSURLSession :
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
AFNetWorking 3.x 使用 NSURLSession 解决了 NSURLConnection 的痛点,从上面的代码可以看出,NSURLSession发起的请求,不再需要在当前线程进行代理方法的回调。可以指定回调的 delegateQueue ,这样我们就不用为了等待代理回调方法而苦苦保活线程了。
同时还要注意一下: 指定的用于接收回调的Queue的 maxConcurrentOperationCount 设为了 1 ,这里目的是想要让并发的请求串行的进行回调。
为什么 3.0 中需要设置为 1 ?
self.operationQueue.maxConcurrentOperationCount = 1;
解答:功能不一样:3.0的operationQueue是用来接收NSURLSessionDelegate回调的,
鉴于一些多线程数据访问的安全性考虑,
设置了maxConcurrentOperationCount = 1 来达到串行回调的效果。
而2.0的operationQueue是用来添加operation并进行并发请求的,所以不要设置为1。
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
为什么要串行回调?
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
//给所要访问的资源加锁,防止造成数据混乱
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
这边对 self.mutableTaskDelegatesKeyedByTaskIdentifier 的访问进行了加锁,目的是保证多线程环境下的数据安全。既然加了锁,就算 maxConcurrentOperationCount 不设为 1,当某个请求正在回调时,下一个请求还是得等待一直到上个请求获取完所要的资源后解锁,所以这边并发回调也是没有意义的。相反多 task 回调导致的多线程并发,还会导致性能的浪费。所以 maxConcurrentOperationCount = 1 。




















暂无评论内容