不支持使用符合协议的具体类型

时间:2018-05-18 21:39:39

标签: swift generics protocol-oriented

我想为设置页面创建通用视图控制器。现在设置来自JSON,但实现可能会在以后关闭,这就是我想拥有协议的原因。例如,LanguageSetting协议是空的,但通过使用它,我仍然可以保留未来的类型安全性,而不必满足于特定的实现(例如JSON解码)。

// Protocols

protocol Query {
    associatedtype Result
    func handleResult(with data: Data) -> Result
}

protocol Setting {
    var name: String { get }
    var icon: URL? { get }
}

protocol LanguageSetting: Setting {
}

protocol CountrySetting: Setting {
}

// Implementations

struct LanguageSettingQuery: Query {
    func handleResult(with data: Data) -> [LanguageSetting] {
        return try! JSONDecoder().decode([JSONLanguageSetting].self, from: data)
    }
}

struct CountrySettingQuery: Query {
    func handleResult(with data: Data) -> [CountrySetting] {
        return try! JSONDecoder().decode([JSONCountrySetting].self, from: data)
    }
}

struct JSONLanguageSetting: LanguageSetting, Decodable {
    var name: String
    var icon: URL?
}

struct JSONCountrySetting: CountrySetting, Decodable {
    var name: String
    var icon: URL?
}

// A generic settings view controller
class LocaleViewController<LocaleQuery: Query>: UIViewController where
LocaleQuery.Result: Sequence, LocaleQuery.Result.Element: Setting {
    private var settingItems = [Setting]()

    init(query: LocaleQuery) {
        settingItems = query.handleResult(with: Data()) as! [Setting]
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

let localeVC = LocaleViewController(query: LanguageSettingQuery())

上面是我在Playgrounds中创建的一个非常简单的实现。问题是最后一行:

let localeVC = LocaleViewController(query: LanguageSettingQuery())

引发错误:

  

使用&#39;语言设置&#39;作为符合协议的具体类型   &#39;设置&#39;不支持

关于如何解决这个问题的任何想法?

旁注:

为什么需要向下倾斜?通用类型约束不足以确保这一点吗?

settingItems = query.handleResult(with: Data()) as! [Setting]

1 个答案:

答案 0 :(得分:0)

错误

协议定义类型;但是,它们与可以定义的其他三种类型(类,结构和枚举)不同,因为它们不能遵循其他协议或要求协议实现某些API。您在此处收到的错误指出了这一点:LanguageSetting协议类型不(也不能)符合通用类中通用where子句Setting所要求的where ... , LocaleQuery.Result.Element: Setting协议LocaleViewController<LocaleQuery: Query>。请注意,协议继承等同于一致性。由于您是通过JSONLanguageSetting中的JSON创建LanguageSettingQuery实例,因此替换

func handleResult(with data: Data) -> [LanguageSetting] {
    return try! JSONDecoder().decode([JSONLanguageSetting].self, from: data)
}

使用

func handleResult(with data: Data) -> [JSONLanguageSetting] {
    return try! JSONDecoder().decode([JSONLanguageSetting].self, from: data)
}

,该错误将得到解决。编译器根据Result方法的返回类型推断相关的[LanguageSetting]类型在前者为[JSONLanguageSetting],在后者为handle(with:)

为什么要投射

编译器强迫您进行强制转换,因为它不能保证泛型类型LocaleQueryArray<Setting>。尽管Array<Setting>确实符合其元素为Sequence类型的Setting协议,但是这样定义的where子句不够不够具体,无法保证{{1} }返回一个数组。仅知道它返回“某种符合查询和序列的类型,其元素类型是设置协议类型”。这很可能是自定义类型。请考虑以下内容:

query.handleData(with: Data())

这将导致程序崩溃,因为它将尝试在struct CustomIterator<T>: IteratorProtocol where T: Setting { typealias Element = T func next() -> T? { nil } } class MyWeirdType<S>: Sequence where S: Setting { typealias Iterator = CustomIterator<S> typealias Element = S func makeIterator() -> CustomIterator<S> { CustomIterator<S>() } } class WeirdQuery<Q>: Query where Q: Setting { typealias Result = MyWeirdType<Q> func handleResult(with data: Data) -> MyWeirdType<Q> { MyWeirdType<Q>() } } let localeVC = LocaleViewController(query: WeirdQuery<JSONLanguageSetting>()) 的初始化程序中将MyWeirdType<JSONLanguageSetting>强制转换为Array<Setting>。如果希望使用数组,请尝试以下操作:

LocaleViewController

注意class LocaleViewController<LocaleQuery: Query>: UIViewController where LocaleQuery.Result == Array<Setting> { private var settingItems = [Setting]() init(query: LocaleQuery) { settingItems = query.handleResult(with: Data()) // Knows that the return is [Setting] super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } 的通用性有所降低,因为where子句比当前更具体。