Swift 5动态数组类型

时间:2019-04-03 18:40:31

标签: swift generics

我在Swift中有以下代码:

class VolumeData <T> {
   private var volume: [T]

   init(with data:Data, width: UInt32, height: UInt32, depth: UInt32) {
      let size = width * height * depth
      volume = data.arrayFromData(size: Int(size))
   }
 }

class Volume: NSObject {

    private var rawVolume : Data?
    public var volume : VolumeData<Any>?
    public var header: Header?

 ....

    func loadVolume(_ name: String) {
        let filepath = Bundle.main.path(forResource: name, ofType: "vol")
        rawVolume = try? Data.init(contentsOf: URL(fileURLWithPath: filepath ?? ""))
        header = Header.init(from: ((rawVolume?.subdata(in: 0..<284))!))
        let volSize = header!.width * header!.height * header!.depth
        switch header!.type {
        case .FLOAT:
             volume = VolumeData<Float32>(with: (rawVolume?.subdata
             (in: 284..<volSize))!, width: header!.width, 
             height: header!.height, depth: header!.depth)
        default:
            volume = nil
        }
    }
}

为了完整起见:

extension Data {
    func arrayFromData<T> (size: Int) -> [T] {
       var arr:[T] = []
       for i in 0..<size {
           let offset = 4*i
           let d:T = self[offset..<offset+4].withUnsafeBytes { $0.pointee }
           arr.append(d)
        }
        return arr
    }
}

现在我有一个编译时错误:

Cannot assign value of type 'VolumeData<Float32>' (aka 'VolumeData<Float>') 
  to type 'VolumeData<Any>?'

任何人都可以为我指出在Swift中实现这种动态泛型的正确方法吗?

1 个答案:

答案 0 :(得分:4)

要在VolumeData类型之间进行转换,可以始终在项目上使用map并强制转换:

class VolumeData<T> {
    private var volume: [T]

    init(with data: Data, width: UInt32, height: UInt32, depth: UInt32) {
        let size = width * height * depth
        volume = data.arrayFromData(size: Int(size))
    }

    private init(volume: [T]) {
       self.volume = volume
    }

    func convert<TargetType>(conversionFn: (T) -> TargetType) -> VolumeData<TargetType> {
        return VolumeData<TargetType>(volume: self.volume.map(conversionFn))
    }
}

然后

case .FLOAT:
    let floatVolume = VolumeData<Float32>(
         with: (rawVolume?.subdata(in: 284..<volSize))!,
         width: header!.width, 
         height: header!.height,
         depth: header!.depth
    )
    volume = floatVolume.convert { $0 as Any }

但是,VolumeData<Any>不是很有用,根本没有用。在这种情况下,最好以某种方式保存原始类型。一种简单的方法是enum,它具有关联的值,例如:

enum VolumeDataType {
   case float(volume: VolumeData<Float32>)
}

然后:

class Volume: NSObject {

    private var rawVolume: Data?
    public var volume: VolumeDataType?
    public var header: Header?

    func loadVolume(_ name: String) {
        let filepath = Bundle.main.path(forResource: name, ofType: "vol")
        rawVolume = try? Data(contentsOf: URL(fileURLWithPath: filepath ?? ""))
        header = Header(from: ((rawVolume?.subdata(in: 0..<284))!))
        let volSize = header!.width * header!.height * header!.depth

        switch header!.type {
        case .FLOAT:
             volume = .float(volume: VolumeData<Float32>(
                 with: (rawVolume?.subdata(in: 284..<volSize))!,
                 width: header!.width, 
                 height: header!.height,
                 depth: header!.depth
             ))
        default:
           volume = nil
        }
    }