在完成块中使用iOS Swift Generics

时间:2017-07-11 09:36:34

标签: ios swift generics block completion

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

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

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

  

错误代码:无法调用类型'(T)的值 - > ()'使用参数列表'(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
}

3 个答案:

答案 0 :(得分:2)

这里没有必要使用泛型。只需将完成处理程序声明为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)
}

答案 1 :(得分:0)

使用以下代码替换ImageLoader函数... 我刚刚改变了

完成:((T) - &gt;())?到
完成:((BaseUIImage) - &gt;())

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

        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
    }
}

答案 2 :(得分:-1)

首先,你的完成被定义为可选的,所以不用它来调用它?会产生错误。 其次,如果你想使用泛型,你应该强制转换为T.

以下是更改的加载程序代码:

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

        let type = getType(forId: id)

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

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