【问题标题】:Problem when transforming a generic type which is an array转换作为数组的泛型类型时出现问题
【发布时间】:2020-02-14 02:50:41
【问题描述】:

我已经建立了一个通用存储库,它返回我要求的资源。在我尝试通过将几个 fetch 调用包装到 DispatchGroup 来执行分组请求之前,这工作得很好。存储库是一个泛型类,指示返回类型。

我将每个单独获取的响应存储到字典中,一旦它们都准备好,我会将字典转换为预期的返回类型。

当泛型是 [Something] 类型(即 T == [Something])时,问题就来了

    public typealias RepoCompletion<T: Codable> = (Result<T?, Error>) -> Void

    public func fetch<T: Entity>(_ resources: [FetchResource<T>], completion: @escaping RepoCompletion<T>) {
        DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
            let group = DispatchGroup()
            let valueSyncQueue = DispatchQueue(label: "Repo.ValueSync")
            var values = [String: T]() 

            for resource in resources {
                group.enter()
                self.localDataSource.fetch(resource) { result in
                    switch result {
                    case .success(let value):
                        if let value = value {
                            valueSyncQueue.sync {
                                values[resource.description] = value
                            }
                        }

                    case .failure(_): break
                    }

                    group.leave()
                }
            }

            group.wait(timeout: .now() + 10)
            let flattenedValues = resources.compactMap({ values[$0.description] }).flatMap({ $0 })
            completion(.success(flattenedValues))
        }
    }

以上代码无法编译。错误显示Member 'success' in 'Result&lt;_?, Error&gt;' produces result of type 'Result&lt;Success, Failure&gt;', but context expects 'Result&lt;_?, Error&gt;'。我认为问题来了是因为泛型类型是 [] 并且编译器需要帮助。

调试时查看类型:

(lldb) po type(of: resources.compactMap({ values[$0.description] }))
Swift.Array<Swift.Array<Schedule>>

(lldb) po type(of: resources.compactMap({ values[$0.description] }).flatMap({ $0 }))
Swift.Array<Schedule>

(lldb) po type(of: flattenedValues)
Swift.Array<Swift.Array<Schedule>>

我不明白为什么表达式let flattenedValues = resources.compactMap({ values[$0.description] }).flatMap({ $0 }) 在评估后左侧和右侧的类型不同:S

假设 T == [时间表]。我希望values 是 [String: [Schedules]]。然后当.compactMap 我期望 [[Schedules]] 的结果,最后.flatMap 返回 [Schedules](即 T)。但是我错过了一些东西,或者我没有得到它。

如果我尝试帮助编译器并强制将 flattenedValues 转换为 as! T,返回时崩溃了

无法将“Swift.Array”(0x1058073b0)类型的值转换为“Schedule”(0x1044ac928)。 2019-10-17 09:48:14.585362+0200 Chicisimo[86448:2426001] 无法将“Swift.Array”(0x1058073b0)类型的值转换为“Schedule”(0x1044ac928)。

我不太明白,因为 T[Schedule] 类型,而不是 Schedule 类型。

任何帮助将不胜感激!谢谢!

【问题讨论】:

  • 可以给我们RepoCompletion的声明吗?
  • 我已经编辑了添加它的问题。

标签: ios arrays swift generics higher-order-functions


【解决方案1】:

您收到此错误是因为您尝试使用 T 数组创建一个 Result,而您的完成需要一个 T 值。

values: [String: T] 在你的 for 循环中建立一个 T 列表:

for resource in resources {
  // here value is a T
  values[resource.description] = value
}

扁平值变量是您的 values 字典的紧凑映射的结果

let flattenedValues = resources.compactMap({ values[$0.description] }).flatMap({ $0 })

这意味着此时flattenedValues的类型是[T]

完成 ((Result&lt;T?, Error&gt;) -&gt; Void) 期望 Result 具有类型为 T 的单个值,但您正尝试使用数组 ([T]) 来解决它。 这就是您收到此错误的原因。

解决方案:

据我了解,您希望从多个资源 (FetchResource) 中获取实体,这意味着您可能希望始终返回一个实体数组 ([T])。

如果是这样,您只需将 RepoCompletion 更改为数组:[T]。这看起来像这样:

// change RepoCompletion<T> to RepoCompletion<[T]>
public func fetch<T: Entity>(_ resources: [FetchResource<T>], completion: @escaping RepoCompletion<[T]>) {
  // ...
}

【讨论】:

  • 我理解抛出的错误。问题是 T 已经是一个数组。也许Entity 让你感到困惑。例如,让我们用Codable 替换它。我不想将结果指定为 [T],我更喜欢在 T 值本身中指示它,因为 Codable 数组已经是 Codable。让我困惑的是当我检查值的类型时,这确实是我不明白的问题。
  • T 实际上不是一个数组,T 是任何符合Codable 的东西。它可以是数组,也可以是String 或自定义struct。您编写代码时考虑到 T 是一个数组,这是不正确的。形成我所看到的,现在您的 fetch 方法可以返回单个结果(例如 structString)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 2021-01-01
  • 1970-01-01
相关资源
最近更新 更多