从0使用Kuikly框架写一个小红书Demo-Day7

通过Kuikly的拓展能力在ios平台实现自定义的图片加载和缓存

我们以iOS平台为例,体验Kuikly框架强大的拓展能力

首先看看Kuikly demo是怎么在iOS原生层面实现图片加载与缓存的:

打开目录:iosAPP -> KuiklyRenderExpand -> Handlers -> KuiklyRenderComponentExpandHandler.m文件

ObjectiveC 复制代码
#import "KuiklyRenderComponentExpandHandler.h"
#import <SDWebImage/UIImageView+WebCache.h>
 
@implementation KuiklyRenderComponentExpandHandler
 
+ (void)load {
    // 注册自定义实现
    [KuiklyRenderBridge registerComponentExpandHandler:[self new]];
}
 
/*
 * 自定义实现设置颜值
 * @param value 设置的颜色值
 * @return 完成自定义处理的颜色对象
 */
- (UIColor *)hr_colorWithValue:(NSString *)value {
    return nil;
}
 
/*
 * 自定义实现设置图片
 * @param url 设置的图片url,如果url为nil,则是取消图片设置,需要view.image = nil
 * @return 是否处理该图片设置,返回值为YES,则交给该代理实现,否则sdk内部自己处理
 */
- (BOOL)hr_setImageWithUrl:(NSString *)url forImageView:(UIImageView *)imageView {
    [imageView sd_setImageWithURL:[NSURL URLWithString:url]
                        completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        
    }];
    return YES;
}
 
/*
 * 自定义实现设置图片(带完成回调,优先调用该方法)
 * @param url 设置的图片url,如果url为nil,则是取消图片设置,需要view.image = nil
 * @param completeBlock 图片加载完成的回调,如有error,会触发loadFailure事件
 * @return 是否处理该图片设置,返回值为YES,则交给该代理实现,否则sdk内部自己处理
 */
- (BOOL)hr_setImageWithUrl:(NSString *)url forImageView:(UIImageView *)imageView complete:(ImageCompletionBlock)completeBlock {
    [imageView sd_setImageWithURL:[NSURL URLWithString:url]
                        completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        if (completeBlock) {
            completeBlock(image, error, imageURL);
        }
    }];
    return YES;
}
 
@end
 

可以看到demo中是通过调用第三库SDWebImage来实现图片的下载与缓存的

我们可以尝试不使用SDWebImage,自行实现图片的下载和缓存,体验Kuikly在不同平台的定制化能力

7.1 扩展机制实现原理

Kuikly框架在设计平台扩展机制时,采用了一种极其巧妙的插件化架构思想。这种设计的核心理念是将框架的核心功能与具体的实现细节完全解耦,让开发者能够在灵活地定制不同平台的特定行为。这种设计哲学体现了现代软件架构中的几个重要原则:开闭原则(对扩展开放,对修改封闭)、依赖倒置原则(高层模块不依赖低层模块,都依赖于抽象)以及单一职责原则(每个组件只负责一个特定的功能)。

Kuikly框架的扩展机制采用了协议驱动的插件化架构,通过定义标准的扩展协议接口,允许开发者实现自定义的组件处理逻辑。

具体实现上,扩展处理器类在+load方法中自动向KuiklyRenderBridge桥接管理器注册自己,框架在运行时通过单例模式管理这些扩展处理器。这种设计实现了零配置的自动注册、完全的实现解耦,使得开发者可以在不修改框架核心代码的情况下,通过实现协议方法来定制图片加载、颜色处理等功能,同时保持了良好的向后兼容性和扩展性。

Kuikly框架定义了KuiklyRenderComponentExpandProtocol协议,允许开发者自定义组件行为:

ObjectiveC 复制代码
@protocol KuiklyRenderComponentExpandProtocol <NSObject>
 
// 必须实现的基础图片加载方法
- (BOOL)hr_setImageWithUrl:(NSString *)url forImageView:(UIImageView *)imageView;
 
@optional
// 可选的带回调图片加载方法(优先调用)
- (BOOL)hr_setImageWithUrl:(NSString *)url 
              forImageView:(UIImageView *)imageView 
                  complete:(ImageCompletionBlock)completeBlock;
 
// 自定义颜色处理
- (UIColor *)hr_colorWithValue:(NSString *)value;
 
// 文本后处理扩展
- (NSString *)kr_customTextWithText:(NSString *)text 
                  textPostProcessor:(NSString *)textPostProcessor;
 
@end
 

扩展处理器通过 +load() 方法实现自动注册,

ObjectiveC 复制代码
@implementation KuiklyRenderComponentExpandHandler
 
+ (void)load {
    // 类加载时自动注册,无需手动调用
    [KuiklyRenderBridge registerComponentExpandHandler:[self new]];
}
 
@end

KuiklyRenderBridge作为中央管理器,负责扩展处理器的注册和获取:

ObjectiveC 复制代码
// 全局静态变量存储扩展处理器实例
static id<KuiklyRenderComponentExpandProtocol> gComponentExpandHandler;
 
@implementation KuiklyRenderBridge
 
+ (void)registerComponentExpandHandler:(id<KuiklyRenderComponentExpandProtocol>)componentExpandHandler {
    gComponentExpandHandler = componentExpandHandler;
}
 
+ (id<KuiklyRenderComponentExpandProtocol>)componentExpandHandler {
    if (!gComponentExpandHandler) {
        // 支持动态创建,通过类名反射
        gComponentExpandHandler = [[NSClassFromString(@"KuiklyRenderComponentExpandHandler") alloc] init];
    }
    return gComponentExpandHandler;
}
 
@end

当我们调用使用在Kuikly框架中使用Image组件的src方法的时候,实际上会调用:

ObjectiveC 复制代码
- (BOOL)p_setImageWithUrl:(NSString *)url {
    BOOL handled = NO;
    
    // 1. 优先调用带回调的扩展方法
    if ([[KuiklyRenderBridge componentExpandHandler] 
         respondsToSelector:@selector(hr_setImageWithUrl:forImageView:complete:)]) {
        
        handled = [[KuiklyRenderBridge componentExpandHandler] 
                   hr_setImageWithUrl:url 
                         forImageView:self 
                             complete:^(UIImage *image, NSError *error, NSURL *imageURL) {
            // 处理加载结果,触发相应事件
            if (error) {
                self.pendingLoadFailure = true;
                self.errorCode = error.code;
            }
        }];
        
    // 2. 降级到基础扩展方法
    } else if ([[KuiklyRenderBridge componentExpandHandler] 
               respondsToSelector:@selector(hr_setImageWithUrl:forImageView:)]) {
        
        handled = [[KuiklyRenderBridge componentExpandHandler] 
                   hr_setImageWithUrl:url forImageView:self];
        
    // 3. 如果没有扩展处理器,抛出断言错误
    } else {
        NSAssert(0, @"should expand hr_setImageWithUrl:forImageView:");
    }
    
    return handled;
}

7.2 在iOS层自定义图片加载

KRImageLoader:统一加载接口层,用于对外提供api接口

KRImageCache:缓存管理层 内存缓存+磁盘缓存 LRU淘汰算法

KRImageDownloader:网络下载层 并发控制

拓展处理器实现,用于对接Kuikle框架调用:

ObjectiveC 复制代码
@implementation KuiklyRenderComponentExpandHandler
 
- (BOOL)hr_setImageWithUrl:(NSString *)url 
              forImageView:(UIImageView *)imageView 
                  complete:(ImageCompletionBlock)completeBlock {
    
    if (!url) {
        // 处理URL为空的情况
        imageView.image = nil;
        [[KRImageLoader sharedLoader] cancelLoadingForImageView:imageView];
        if (completeBlock) {
            completeBlock(nil, nil, nil);
        }
        return YES;
    }
    
    // 使用自定义图片加载器
    [[KRImageLoader sharedLoader] loadImageForImageView:imageView
                                                withURL:[NSURL URLWithString:url]
                                            placeholder:nil
                                               progress:nil
                                              completed:^(UIImage *image, NSError *error, NSURL *imageURL) {
        // 将结果回调给Kuikly框架
        if (completeBlock) {
            completeBlock(image, error, imageURL);
        }
    }];
    
    return YES; // 表示已处理
}
 
@end

7.2.1 缓存系统设计(双重缓存策略)

1、 内存缓存

通过NSCache自动管理内存

2、 磁盘缓存

手动实现了LRU缓存策略,并使用异步的IO操作,避免阻塞接口

接口定义,具体实现可以查看仓库

ObjectiveC 复制代码
/**
 * KRImageCache - 图片缓存管理器
 * 
 * 功能特性:
 * - 双重缓存策略:内存缓存(NSCache) + 磁盘缓存(文件系统)
 * - 自动内存管理:响应内存警告自动清理
 * - 后台清理:应用进入后台时清理过期缓存
 * - 线程安全:使用串行队列保证磁盘操作安全
 */
@interface KRImageCache : NSObject
 
/**
 * 获取缓存管理器的单例实例
 * @return 缓存管理器单例
 */
+ (instancetype)sharedCache;
 
/**
 * 同步获取缓存图片(仅从内存缓存获取)
 * @param key 缓存键值
 * @return 缓存的图片对象,如果不存在则返回nil
 */
- (UIImage * _Nullable)imageForKey:(NSString *)key;
 
/**
 * 异步获取缓存图片(先检查内存缓存,再检查磁盘缓存)
 * @param key 缓存键值
 * @param completion 完成回调,在主线程执行
 */
- (void)imageForKey:(NSString *)key completion:(void(^)(UIImage * _Nullable image))completion;
 
/**
 * 同步存储图片到缓存(内存+磁盘)
 * @param image 要缓存的图片
 * @param key 缓存键值
 */
- (void)storeImage:(UIImage *)image forKey:(NSString *)key;
 
/**
 * 异步存储图片到缓存(内存+磁盘)
 * @param image 要缓存的图片
 * @param key 缓存键值
 * @param completion 存储完成回调,在主线程执行
 */
- (void)storeImage:(UIImage *)image forKey:(NSString *)key completion:(void(^ _Nullable)(void))completion;
 
/**
 * 清理内存缓存
 */
- (void)clearMemoryCache;
 
/**
 * 清理磁盘缓存
 */
- (void)clearDiskCache;
 
/**
 * 清理所有缓存(内存+磁盘)
 */
- (void)clearAllCache;
 
 
/**
 * 异步获取磁盘缓存大小
 * @param completion 完成回调,在主线程执行,参数为磁盘缓存占用的字节数
 */
- (void)diskCacheSizeWithCompletion:(void(^)(NSUInteger size))completion;
 
/**
 * 当缓存超过限制时,按LRU策略删除最旧的文件
 */
- (void)cleanDiskCacheWithSizeLimit;
 
/**
 * 根据URL生成缓存键值
 * @param url 图片URL
 * @return 生成的缓存键值(SHA256哈希)
 */
+ (NSString *)cacheKeyForURL:(NSURL *)url;
 
@property (nonatomic, strong) NSCache *memoryCache;        // 内存缓存,自动管理内存使用
@property (nonatomic, strong) dispatch_queue_t ioQueue;    // 串行IO队列,保证磁盘操作线程安全
@property (nonatomic, copy) NSString *diskCachePath;       // 磁盘缓存目录路径
@property (nonatomic, assign) BOOL isCleaningDiskCache;    // 是否正在清理磁盘缓存,避免重复清理
@property (nonatomic, strong) NSDate *lastCleanupTime;     // 上次清理时间,避免频繁清理
 
@end

7.2.2 下载器设计

采用单例模式管理NSURLSession,通过任务合并机制避免重复下载相同URL的图片,支持最大6个并发下载、15秒超时控制、精确的ImageView关联取消机制,并使用barrier队列确保线程安全的任务管理,为图片加载框架提供了可靠的网络下载能力。

接口定义,具体实现可以查看仓库

ObjectiveC 复制代码
/**
 * KRImageDownloader - 图片下载器
 *
 */
@interface KRImageDownloader : NSObject
 
/**
 * 获取下载器的单例实例
 * @return 下载器单例
 */
+ (instancetype)sharedDownloader;
 
/**
 * 下载图片
 * @param url 图片URL
 * @param progressBlock 下载进度回调(可选)
 * @param completedBlock 下载完成回调(可选)
 * @return 下载任务对象,可用于取消下载
 */
- (NSURLSessionDataTask * _Nullable)downloadImageWithURL:(NSURL *)url
                                                progress:(KRDownloadProgressBlock _Nullable)progressBlock
                                               completed:(KRDownloadCompletionBlock _Nullable)completedBlock;
 
/**
 * 为特定ImageView下载图片
 * @param url 图片URL
 * @param imageView 关联的ImageView,用于精确取消
 * @param progressBlock 下载进度回调(可选)
 * @param completedBlock 下载完成回调(可选)
 * @return 下载任务对象,可用于取消下载
 */
- (NSURLSessionDataTask * _Nullable)downloadImageWithURL:(NSURL *)url
                                               imageView:(UIImageView *)imageView
                                                progress:(KRDownloadProgressBlock _Nullable)progressBlock
                                               completed:(KRDownloadCompletionBlock _Nullable)completedBlock;
 
/**
 * 取消指定URL的下载任务
 * @param url 要取消的图片URL
 */
- (void)cancelDownloadForURL:(NSURL *)url;
 
/**
 * 为特定ImageView取消下载任务
 * 只移除该ImageView对应的回调,不影响其他ImageView
 * @param url 图片URL
 * @param imageView 要取消的ImageView
 */
- (void)cancelDownloadForURL:(NSURL *)url imageView:(UIImageView *)imageView;
 
/**
 * 取消所有下载任务
 */
- (void)cancelAllDownloads;
 
/**
 * 设置最大并发下载数量
 * @param maxConcurrentDownloads 最大并发数
 */
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads;
 
/**
 * 设置下载超时时间
 * @param timeout 超时时间(秒)
 */
- (void)setDownloadTimeout:(NSTimeInterval)timeout;
 
@end

7.3 小结

通过自定义图片的加载和缓存,可以体验到Kuikly框架极强的拓展能力,尤其在平台定制化。以iOS平台自定义图片加载与缓存为例,无论是集成第三方库如SDWebImage,还是使用自研方案,开发者都可以通过Kuikly的插件化架构和协议驱动机制,实现与核心逻辑完全解耦的功能扩展。各类自定义组件只需遵循标准协议,通过零配置自动注册机制即可无缝集成到系统。这种设计不仅保障了应用的灵活性和向后兼容性,还让每个平台都能根据自身需求精准定制,充分体现了Kuikly框架开放、高效和可持续的扩展能力。

相关推荐
拾心212 小时前
【云运维】LNMP 架构部署与应用
运维·架构
qq_167401515 小时前
使用 dash 构建整洁架构应用
架构·dash
绝无仅有5 小时前
某多多大厂面试相关计算机网络知识点总结
后端·面试·架构
绝无仅有5 小时前
调用服务出现网络错误的问题排查与解决
后端·面试·架构
存在morning15 小时前
【人工智能学习笔记 三】 AI教学之前端跨栈一:React整体分层架构
笔记·学习·架构
canonical_entropy16 小时前
最小信息表达:从误解到深层理解的五个关键点
后端·架构
蚂小蚁20 小时前
一文吃透:宏任务、微任务、事件循环、浏览器渲染、Vue 批处理与 Node 差异(含性能优化)
前端·面试·架构
吃饺子不吃馅20 小时前
前端画布类型编辑器项目,历史记录技术方案调研
前端·架构·github