SDWebImage 中的单例模式应用
1. 核心单例类
SDWebImage 使用单例模式的 典型场景:
- SDImageCache: 全局管理内存与磁盘缓存
- SDWebImageManager: 协调图片加载流程(下载器+缓存+解码器)
- SDWebImageDownloader: 网络下载任务调度
2. 单例实现关键代码
以 SDImageCache 为例:
SDImageCache.h
@interface SDImageCache : NSObject
// 单例访问类方法
+ (nonnull instancetype)sharedImageCache;
@end
SDImageCache.m
@implementation SDImageCache {
NSCache *_memoryCache; // 内存缓存
NSString *_diskCachePath; // 磁盘缓存路径
NSFileManager *_fileManager; // 文件操作
}
// 单例内核实现
+ (nonnull instancetype)sharedImageCache {
static SDImageCache *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
// 初始化资源
- (instancetype)init {
self = [super init];
if (self) {
// 初始化内存缓存(容量可配置)
_memoryCache = [[NSCache alloc] init];
_memoryCache.name = @"com.sdwebimage.cache";
// 初始化磁盘缓存目录
_diskCachePath = [self defaultDiskCachePath];
_fileManager = [NSFileManager new];
}
return self;
}
@end
单例模式核心关键点
1. 线程安全初始化
- 使用
dispatch_once确保单例在 多线程环境下唯一初始化 - 避免
@synchronized的性能损耗
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ /* 初始化代码 */ });
2. 全局访问入口
- 提供
+sharedInstance类方法作为唯一访问点 - 禁止用户通过
alloc/new/copy创建新实例
//SDImageCache 没有这么实现,不过可以借鉴用来实现绝对单例
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
return [self sharedImageCache]; // 或断言禁止
}
3. 资源封装
- 内存缓存:使用
NSCache(自动管理内存) - 磁盘缓存:指定沙盒目录存储(独立线程管理 IO)
4. 配置灵活性
- 允许自定义初始化参数
@interface SDImageCache : NSObject
// 带命名空间的自定义初始化(允许创建多个单例)
+ (nonnull instancetype)sharedImageCacheWithNamespace:(NSString *)ns;
@end
对日常开发的启示
1. 单例适用场景
- 全局唯一资源控制:如缓存管理、网络监控、日志系统
- 高频访问的服务:如位置管理、音频播放控制器
2. 单例设计建议
| 关键点 | 正确做法 | 错误做法 |
|---|---|---|
| 线程安全 | 使用 dispatch_once | 直接 if (!instance) |
| 内存管理 | 单例生命周期绑定整个应用 | 手动过早释放 |
| 可测试性 | 允许传递协议对象(依赖注入) | 硬编码依赖 |
| 可扩展性 | 允许通过派生类或配置修改行为 | 完全封闭内部实现 |
3. 单例优化示例:销毁重建
解决 多用户切换需重置缓存 的场景:
// 扩展单例支持销毁
@interface SDImageCache (Destroyable)
+ (void)destroySharedInstance;
@end
@implementation SDImageCache (Destroyable)
+ (void)destroySharedInstance {
onceToken = 0; // 重置 token
instance = nil; // 释放资源
}
@end
4. 替代方案:依赖注入
避免滥用单例,对于需要灵活替换的模块,采用协议 + 依赖注入:
// 定义缓存协议
@protocol ImageCacheProtocol <NSObject>
- (void)storeImage:(UIImage *)image forKey:(NSString *)key;
@end
// 通过属性配置而非全局访问
@interface MyViewController : UIViewController
@property (nonatomic, strong) id<ImageCacheProtocol> imageCache;
@end
总结
SDWebImage 单例技术的收益
- 资源统一管控:集中管理内存和磁盘缓存
- 性能高效:避免重复创建/销毁高频访问对象
- 接口统一:简化调用复杂度
开发注意事项
- 单例不应持有过多外部对象(防止循环引用)
- 升级到 Swift 时优先考虑
static let+final class - 合理划分职责(避免「上帝单例」)
(ps: 以上大部分内容使用 DeepSeek R 生成,作者有部分内容调整,如有任何不正确之处欢迎指正)