用DeepSeek学源码-SDImageCodersManager 中的工厂模式解析

455 阅读4分钟

SDImageCodersManager 中的工厂模式解析

1. 工厂模式类型

SDImageCodersManager 应用了 抽象工厂模式(Abstract Factory Pattern) 的变体,更具体地属于 多态工厂模式(Pluggable Factory)
其核心特征:

  • 统一的抽象接口SDImageCoder 协议)定义编/解码能力。
  • 动态注册多组具体工厂(不同编/解码器的实现类)。
  • 运行时按需选择适配的工厂(根据图片数据格式)。

2. 工厂模式应用步骤

(1) 定义抽象产品接口(Protocol-Based)
  • 抽象产品接口SDImageCoder 协议
    声明所有编/解码器必须实现的核心方法:

    #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)
  • 具体产品实现:多种编/解码器类
    针对不同图片格式实现协议,例如:

    // 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
    统一管理所有注册的编/解码器实例,并根据数据类型选择对应工厂:

    #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. 模式运行流程

  1. 初始化阶段

    • 注册编/解码器到 SDImageCodersManager

      // 注册自定义编解码器
      SDImageWebPCoder *webPCoder = [[SDImageWebPCoder alloc] init];
      [[SDImageCodersManager sharedManager] addCoder:webPCoder];
      
  2. 请求处理阶段

    • 客户端传递图片数据给 coder(for:)

      // 查询可用编解码器
      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 生成,作者有部分内容调整,如有任何不正确之处欢迎指正)