【问题标题】:Using iOS Swift Generics in Completion blocks在完成块中使用 iOS Swift 泛型
【发布时间】:2017-07-11 09:36:34
【问题描述】:

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

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

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

错误代码:无法使用参数列表“(FrontImage)”调用类型“(T) -> ()”的值

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

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
}

【问题讨论】:

  • 你为什么使用泛型?您可以简单地将loadImage 声明为func loadImage(forId id: UUID, completion: ((BaseImage) -&gt; ())?)

标签: ios swift generics block completion


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    完成:((T) -> ())?到
    完成:((BaseUIImage) -> ())

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

    【讨论】:

      【解决方案3】:

      首先,你的完成被定义为可选的,所以调用它时不带 ?会产生错误。 其次,如果你想使用泛型,你应该强制转换为 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
          }
      }
      

      【讨论】:

      • > 其次,如果你想使用泛型,你应该强制转换为 T。为什么?
      • 看起来编译器不明白 FrontImage 是 T:BaseUIImage。我同意另一条评论,您可以在这里摆脱泛型,只需使用 BaseImage 完成。为什么要投反对票?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多