SDImageCodersManager 中的工厂模式解析
1. 工厂模式类型
SDImageCodersManager 应用了 抽象工厂模式(Abstract Factory Pattern) 的变体,更具体地属于 多态工厂模式(Pluggable Factory) 。
其核心特征:
- 统一的抽象接口 (
SDImageCoder
协议)定义编/解码能力。 - 动态注册多组具体工厂(不同编/解码器的实现类)。
- 运行时按需选择适配的工厂(根据图片数据格式)。
2. 工厂模式应用步骤
(1) 定义抽象产品接口(Protocol-Based)
-
抽象产品接口 :
SDImageCoder
协议声明所有编/解码器必须实现的核心方法:
OBJECTIVEC#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @protocol SDImageCoder <NSObject> /// 判断是否能解码指定数据 + (BOOL)canDecodeFromData:(NSData *)data; /// 解码数据生成图片 - (nullable UIImage *)decodedImageWithData:(NSData *)data options:(nullable NSDictionary<SDImageCoderOptionKey, id> *)options; // 其他方法(编码、渐进式解码等)... @end NS_ASSUME_NONNULL_END
(2) 创建具体产品类(Concrete Product)
-
具体产品实现 :多种编/解码器类
针对不同图片格式实现协议,例如:
OBJECTIVEC// WebP 编/解码器 #import "SDImageCoder.h" @interface SDImageWebPCoder : NSObject <SDImageCoder> @end #import "SDImageWebPCoder.h" @implementation SDImageWebPCoder + (BOOL)canDecodeFromData:(NSData *)data { // 检查 WebP Magic Number if (data.length < 12) return NO; uint32_t magic = 0; [data getBytes:&magic length:4]; return magic == 0x52494646; // RIFF 标志位(WebP 文件头) } - (nullable UIImage *)decodedImageWithData:(NSData *)data options:(nullable NSDictionary<SDImageCoderOptionKey, id> *)options { // 实际调用 WebP 解码库(如 libwebp)的代码 // ... return decodedImage; } @end // GIF 编/解码器 #import <Foundation/Foundation.h> #import "SDImageCoder.h" @interface SDImageGIFCoder : NSObject <SDImageCoder> @end #import "SDImageGIFCoder.h" #import <ImageIO/ImageIO.h> // 使用 ImageIO 框架解析 GIF @implementation SDImageGIFCoder #pragma mark - SDImageCoder Protocol + (BOOL)canDecodeFromData:(NSData *)data { // 检查 GIF Magic Number ("GIF8") if (data.length < 6) return NO; unsigned char magic[3]; [data getBytes:magic length:3]; return (magic[0] == 'G' && magic[1] == 'I' && magic[2] == 'F'); } - (nullable UIImage *)decodedImageWithData:(NSData *)data options:(nullable NSDictionary<SDImageCoderOptionKey, id> *)options { // 使用 ImageIO 解析 GIF 数据 CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); if (!source) return nil; size_t frameCount = CGImageSourceGetCount(source); if (frameCount == 0) { CFRelease(source); return nil; } // 处理多帧 GIF 动画 NSMutableArray<UIImage *> *frames = [NSMutableArray array]; NSTimeInterval totalDuration = 0; for (size_t i = 0; i < frameCount; i++) { CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, i, NULL);
(3) 构建工厂管理者(Factory Manager)
-
工厂管理类 :
SDImageCodersManager
统一管理所有注册的编/解码器实例,并根据数据类型选择对应工厂:
OBJECTIVEC#import <Foundation/Foundation.h> #import "SDImageCoder.h" @interface SDImageCodersManager : NSObject /// 单例入口 @property (class, nonatomic, readonly) SDImageCodersManager *sharedManager; /// 添加编解码器(优先级越高越先被调用) - (void)addCoder:(id<SDImageCoder>)coder; /// 移除编解码器 - (void)removeCoder:(id<SDImageCoder>)coder; /// 根据数据匹配可用的编解码器 - (nullable id<SDImageCoder>)coderForData:(NSData *)data; @end #import "SDImageCodersManager.h" @interface SDImageCodersManager () @property (nonatomic, strong) NSMutableArray<id<SDImageCoder>> *coders; @end @implementation SDImageCodersManager - (instancetype)init { if (self = [super init]) { // 注册常用解码器 _imageCoders = [NSMutableArray arrayWithArray:@[[SDImageIOCoder sharedCoder], [SDImageGIFCoder sharedCoder], [SDImageAPNGCoder sharedCoder]]]; SD_LOCK_INIT(_codersLock); } return self; } - (instancetype)init { self = [super init]; if (self) { _coders = [NSMutableArray array]; } return self; } //注册更多解码器,如:SDImageAWebPCoder、SDImageHEICCoder - (void)addCoder:(id<SDImageCoder>)coder { if (![self.coders containsObject:coder]) { [self.coders insertObject:coder atIndex:0]; // 插入到最前面(优先级最高) } } - (void)removeCoder:(id<SDImageCoder>)coder { [self.coders removeObject:coder]; } - (nullable id<SDImageCoder>)coderForData:(NSData *)data { if (!data) return nil; for (id<SDImageCoder> coder in self.coders) { if ([coder.class canDecodeFromData:data]) { return coder; } } return nil; } @end
3. 模式结构对照表
工厂模式角色 | SDImageCodersManager 中的实现 |
---|---|
抽象工厂 (Abstract Factory) | SDImageCoder 协议(定义工厂接口) |
具体工厂 (Concrete Factory) | 实现 SDImageCoder 的类(如 SDImageWebPCoder ) |
工厂管理器 (Factory Manager) | SDImageCodersManager 类 |
客户端 (Client) | 使用 SDImageCodersManager 的调用方 |
4. 模式运行流程
-
初始化阶段:
-
注册编/解码器到
SDImageCodersManager
:OBJECTIVEC// 注册自定义编解码器 SDImageWebPCoder *webPCoder = [[SDImageWebPCoder alloc] init]; [[SDImageCodersManager sharedManager] addCoder:webPCoder];
-
-
请求处理阶段:
-
客户端传递图片数据给
coder(for:)
:OBJECTIVEC// 查询可用编解码器 NSData *imageData = ...; // 输入图片二进制数据 id<SDImageCoder> coder = [[SDImageCodersManager sharedManager] coderForData:imageData]; UIImage *image = [coder decodedImageWithData:imageData options:nil];
-
内部基于数据类型动态选择
SDImageWebPCoder
等具体实现。
-
5. 关键设计优势
- 扩展性 :新增图片格式支持时,只需添加新的
SDImageCoder
实现类并注册,无需修改现有逻辑。 - 解耦性:客户端无需关心具体编/解码器实现,仅依赖抽象协议。
- 灵活性:可通过运行时动态调整注册的编/解码器(如禁用 GIF 支持)。
6. 差异说明:与经典工厂模式的对比
对比项 | 经典工厂模式 | SDImageCodersManager |
---|---|---|
工厂类型 | 每个工厂类生产一种固定类型 | 多工厂共存,动态选择 |
工厂注册方式 | 编译时硬编码 | 运行时动态注册(addCoder/removeCoder ) |
适用范围 | 产品类型预知且有限 | 未知/动态扩展的第三方格式支持 |
总结
SDImageCodersManager 将抽象工厂模式 与注册机制结合,形成一种灵活的插件化架构。通过协议定义标准化编/解码接口,允许第三方开发者无缝扩展新格式支持,完美契合图像处理场景的多格式需求。这种设计模式是 SDWebImage 能长期保持 iOS 生态领跑地位的重要原因之一。
(ps: 以上大部分内容使用 DeepSeek R 生成,作者有部分内容调整,如有任何不正确之处欢迎指正)