这一切都始于`协议只能用作通用约束,因为它具有自我或相关的类型要求

时间:2018-03-10 20:53:31

标签: swift type-erasure

我正在处理一个小问题:同时获取一堆有序数据并以正确的顺序输出(具体来说,获取.ts个文件的负载,通过{{1按照正确的顺序,但这是偶然的)。想要将重新排序(按顺序传递碎片)和管道(让ffmpeg做它的事情)分开,我把它写成两个对象:

ffmpeg

struct Aggregator<ChunkType> {

    private var currentIdx = 0
    private var chunks: [Int:ChunkType] = [:]
    private let serializer: Serializer

    init(serializer: Serializer) where Serializer.ChunkType == ChunkType {
        // self.serializer = serializer
    }

    private let queue = DispatchQueue(label: "Serial")

    mutating func accept(chunk:ChunkType, forIndex idx: Int) {
        // One accessor at a time
        queue.sync {
            chunks[idx] = chunk

            // Forward zero or more chunks
            while chunks[currentIdx] != nil {
                //serializer.append(chunk: chunks.removeValue(forKey: currentIdx))
                currentIdx += 1
            }
        }
    }
}

有两个实现:

protocol Serializer {
    associatedtype ChunkType
    func append(chunk: ChunkType)
}

最终目标是使用看起来像:

// For capturing the output of an Aggregator<Int> to check it's working as intended
class IntArraySerializer: Serializer {
    typealias ChunkType = Int

    var allInts = [Int]()

    func append(chunk: ChunkType) {
        allInts.append(chunk)
    }
}

// For piping output through ffmpeg
class FFMPEGSerializer: Serializer {
    typealias ChunkType = Data

    private let ffmpegBin = "/usr/local/bin/ffmpeg"
    private let ffmpeg: Process
    private let stdin: Pipe

    init(args: [String]) throws {
        ffmpeg = Process()
        ffmpeg.launchPath = ffmpegBin
        ffmpeg.arguments = args
        stdin = Pipe()
        ffmpeg.standardInput = stdin
    }

    func append(chunk: Data) {
        stdin.fileHandleForWriting.write(chunk)
    }
}

然而,我对相关类型,泛型和可能的类型擦除的理解(如果确实是这里的解决方案)阻止我最终确定let ffmpeg = FFMPEGSerializer(["-i", "-", "-c", "copy", "-bsf:a aac_adtstoasc", "output.mp4"]) let aggregator = Aggregator(serializer: ffmpeg) for (idx, url) in someListOfUrls.enumerated() { concurrentQueue.async { fetch(url) { data in aggregator.accept(chunk: data) } } } ,从各种注释行可以看出。我已经完成了关于类型擦除的一些阅读(例如this Big Nerd Ranch article),但我还没有理解我是否应该尝试在我的情况下应用它。

将其归结为一个陈述:我希望Aggregator的{​​{1}}通知ChunkType的泛型类型。

1 个答案:

答案 0 :(得分:0)

由于错误消息明确说明,您无法使用协议Serializer来注释属性或参数的类型。

我不明白你对 type-erasure 的意思,所以我可能会误解你的意思,但就我读你的代码而言,你可以轻松修复你的Aggregator使用泛型:

//Make `S` a generic parameter constrained to `Serializer`
struct Aggregator<S: Serializer> {
    //`ChunkType` needs always to be the same type as `S.ChunkType`
    typealias ChunkType = S.ChunkType

    private var currentIdx = 0
    private var chunks: [Int: ChunkType] = [:]
    //You can use `S` as a type of properties...
    private let serializer: S

    //...or a type of parameters.
    init(serializer: S) {
        self.serializer = serializer
    }

    private let queue = DispatchQueue(label: "Serial")

    mutating func accept(chunk: ChunkType, forIndex idx: Int) {
        // One accessor at a time
        queue.sync {
            chunks[idx] = chunk

            // Forward zero or more chunks
            while chunks[currentIdx] != nil {
                serializer.append(chunk: chunks.removeValue(forKey: currentIdx)!)
                currentIdx += 1
            }
        }
    }
}