CoreData、UIImage 和展开

CoreData, UIImage and Unwrapping

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

因为实例化 UIImage 可能会失败并且必须展开,并且因为核心数据属性都是可选的,所以我进入了一个 if-pyramid/unwrapping 循环,所以我似乎要解决这个问题不雅

(这种复杂性的部分原因是从将图像存储在资产目录中迁移到将它们存储在 CoreData/filesystem 中,但我需要确保我可以在所有这些方案之间进行通信。)

这是我的 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")!