CoreData、UIImage 和解包

问题描述

如果属性为零,我的应用程序需要显示从 CoreData 实体中提取的图像,并具有 2 个回退级别。首先,它查看 Entity.bannerImageBlob 属性(二进制数据/外部存储)并尝试从中构建 UIImage。如果失败,它会在资产目录中查找名为 Entity.bannerImageName 的资产,如果不存在,则使用资产目录中存在的“banner-placeholder”。

因为实例化 UIImage 可能会失败并且必须被解包,而且因为核心数据属性都是可选的,所以我进入了一个 if-pyramid/unwrapping 循环,以至于我似乎不雅地处理这个问题。

(其中一些复杂性是从将图像存储在资产目录中迁移到将它们存储在 CoreData/文件系统中的结果,但我需要确保我可以在所有这些方案之间进行通信。)

这是我的 CoreData 实体扩展中的便利变量。实体属性一个尾随下划线。这就是我希望它工作的方式(此代码不起作用)。


var bannerImage: Image {
    let fallback = UIImage(named: bannerImageName_ ?? "banner-placeholder")
    
    if let blobImage = UIImage(data: bannerImageBlob_) { // error: data? must be unwrapped...
        return Image(uiImage: blobImage)
    } else {
        return Image(uiImage: fallback) // error: UIImage? must be unwrapped...
    }
}

这似乎创建了无休止的合并认值级联。有没有更好的办法?

解决方法

确实没有更优雅的解决方案,但您可以将其简化为单一级别的 if-else 语句:

var bannerImage: Image {
    if let data = bannerImageBlob_,let let blobImage = UIImage(data: data) {
        return Image(uiImage: blobImage)
    } else if let imageName = bannerImageName_,let image = UIImage(named: imageName) {
        return Image(uiImage: image)
    } else {
        let fallback = UIImage(named: "banner-placeholder")!
        return Image(uiImage: fallback)
    }
}

您可能希望将图像缓存在内存中,而不是每次都计算,如果您有一个字符串 someUniqueId_ 可以用作键,它可能看起来像这样:

static fileprivate let imageCache = NSCache<NSString,UIImage>()

var bannerImage: Image {
    if let cachedImage = Self.imageCache.object(forKey: someUniqueId_) {
        return Image(uiImage: cachedImage)
    } else {
        let uiImage: UIImage = {
            if let data = bannerImageBlob_,let let blobImage = UIImage(data: data) {
                return blobImage
            } else if let imageName = bannerImageName_,let image = UIImage(named: imageName) {
                return image
            } else {
                return UIImage(named: "banner-placeholder")!
            }
        }()
        Self.imageCache.setObject(uiImage,forKey: someUniqueId_)
        return Image(uiImage: uiImage)
    }
}
,

为 UIImage 创建一个扩展并添加带有可选参数的新工厂方法

extension UIImage {
    static func create(data: Data?) -> UIImage? {
         guard let data = data else { return nil }
         return UIImage(data: data)
    }

    static func create(named: String?) -> UIImage? {
         guard let string = named else { return nil }
         return UIImage(named: string)
    }
}

然后

return UIImage.create(data: bannerImageBlog_) ??
    UIImage.create(named: bannerImageName_) ??
    UIImage(named: "banner-placeholder")!