ios - 在完成 block 中使用 iOS Swift 泛型

标签 ios swift generics block completion

我需要从资源加载原始图像并将其转换为 UIImage 稍后将存储在图像缓存中。

每个图像都有一个标识符,用作缓存中的键。

当我试图用泛型解决这个问题时,我得到了这个奇怪的错误 尝试在完成中返回通用对象时。

Errorcode: Cannot invoke value of type '(T) -> ()' with argument list '(FrontImage)'

下面是重现错误的示例代码。

import UIKit

protocol CacheableImage {
    static func getIdentifier() -> String
}

struct Image {
    var imageData: Data = Data()
}

class BaseUIImage: CacheableImage {
    class func getIdentifier() -> String {
        return ""
    }

    var image: UIImage?

    required init(){}
}

class FrontImage: BaseUIImage {
    override class func getIdentifier() -> String {
        return "FrontImage"
    }
}

class BackImage: BaseUIImage {
    override class func getIdentifier() -> String {
        return "BackImage"
    }
}

class ImageLoader<T: BaseUIImage> {
    func loadImage(forId id: UUID, completion: ((T) -> ())?) {

        let type = getType(forId: id)

        switch type {
        case .frontImage:
            let image = FileSystemResourceLoader.loadResource(forId: id)
            let frontUIImage: FrontImage  = ImageConverter.convertToUIImage(image: image)
            completion(frontUIImage)
        case .backImage:
            let image = FileSystemResourceLoader.loadResource(forId: id)
            let backUIImage: BackImage  = ImageConverter.convertToUIImage(image: image)
            completion(backUIImage)
        }
    }

    func getType(forId id: UUID) -> ImageType {
        return .frontImage // simplified return value
    }
}

class ImageConverter {
    static func convertToUIImage<T: BaseUIImage>(image: Image) -> T {
        return T() // simplified return value
    }
}


class FileSystemResourceLoader {
    static func loadResource(forId id: UUID) -> Image {
        return Image()
    }
}

enum ImageType {
    case frontImage
    case backImage
}

最佳答案

这里不需要使用泛型。只需声明您的完成处理程序采用 BaseImage。它还允许您将对它的调用排除在开关之外

func loadImage(forId id: UUID, completion: ((BaseImage) -> ())?) {

    let type = getType(forId: id)

    let image = FileSystemResourceLoader.loadResource(forId: id)
    let resultImage: BaseImage
    switch type {
    case .frontImage:
        resultImage = FrontImage(image: image)
    case .backImage:
        resultImage = BackImage(image: image)
    }
    completion?(resultImage)
}

事实上,您可以很容易地取消图像类型枚举(这有点代码味道)。一种方法是更改​​ getType 以返回创建图像的闭包。

func getConstructor(forId id: UUID) -> ((Image) -> BaseImage)
{
    if id == "foo" // Jut an example
    {
        return { BackImage($0) }
    }
    else
    {
        return { FrontImage($0) }
    }
}

然后你的loadImage就变成了

func loadImage(forId id: UUID, completion: ((BaseImage) -> ())?) {

    let constructor= getConstructor(forId: id)

    let image = FileSystemResourceLoader.loadResource(forId: id)
    let resultImage = constructor(image)
    completion?(resultImage)
}

关于ios - 在完成 block 中使用 iOS Swift 泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45030750/

相关文章:

ios - 为什么 UILongPressGestureRecognizer 返回两次

ios - 在xcode模拟器中测试phonegap条码扫描仪插件

java - 泛型方法 Java

java - 泛型类型参数给出错误,但通配符类型参数不会给出错误

function - Typescript 中的通用函数和推断通用键

ios - 未从 Collection View 调用 prepareForSegue

ios - 收到自定义 collectionView 错误 : EXC_BAD_ACCESS code=2

ios - WCSession:使用 transferUserInfo 或 sendMessage 的最佳方式?

swift - 无法将 UITableViewCell 中的单元格数据推送到新的 UITableViewController

swift - 使用 Kingfisher 时重新下载不同尺寸的图像