SDWebImage 单例使用不规范导致图片缓存错乱问题

413 阅读2分钟

前言

在 SDWebImage 中,有一个 SDWebImageCacheKeyFilter 的协议可以让开发者来自定义 CacheKey,而不是使用默认的 url 当 key。

下面的代码示例来自官方:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {
        url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
        return [url absoluteString];
    };

    // Your app init code...
    return YES;
}

遇到的问题

我是开发主项目中的一个模块,自己对 sd 的调用进行了一个简单的包装:

- (void)custom_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:SDWebImageAllowInvalidSSLCertificates context:nil];
}

开发中,就是正常调用上面的方法去异步加载图片。但是代码提交测试后,测试反馈了一个 Bug:在主工程的设置页面,修改用户头像后,我开发的模块中,所有用 sd 加载的图片都会变成用户的新头像。

在我确定我这边代码没有什么问题后,就查看了主工程的代码。发现了主工程在用户头像修改后会自定义 SDWebImage 的 filterCacheKey:

NSString *headUrl = [NSString stringWithFormat:@"customprotocol://%@",@"image"];
SDWebImageCacheKeyFilter *cacheKey = [SDWebImageCacheKeyFilter cacheKeyFilterWithBlock:^NSString * _Nullable(NSURL * _Nonnull url) {
    return headUrl;
}];
    
[[SDWebImageManager sharedManager] setCacheKeyFilter:cacheKey];

上面的代码虽然是按照官方示例写的。但它会将所有 sd 缓存图片的 key 都替换成 headUrl。

通过 sd 的源码也可以验证这一点:

截屏2022-03-11 下午4.08.37.png 当全局修改 filter 后,我的模块中图片缓存的 key 也会变成修改的 filter,这也就造成了模块中的图片全部变成用户头像的问题。

解决方案

解决方案如下:

SDWebImageCacheKeyFilter *filter = [SDWebImageCacheKeyFilter cacheKeyFilterWithBlock:^NSString * _Nullable(NSURL * _Nonnull url) {
    return url.absoluteString;
}];
SDWebImageContext *context = @{ SDWebImageContextCacheKeyFilter: filter };
[self sd_setImageWithURL:url placeholderImage:placeholder options:SDWebImageAllowInvalidSSLCertificates context:context];

上述代码的作用:重新将 absoluteString 这是为图片的 cacheKey,并绑定到 context 上。这样,图片的本身 URL 的地址就会成图片的 cacheKey 了。

使用 context 而不适用 sharedManager 的原因如下:sharedManager 为单例,修改它的属性会影响整个项目。对于多模块的项目会造成不可预料的问题。

所以,在多模块项目中,对单例的修改,我们应该慎之又慎。