我想为设置页面创建通用视图控制器。现在设置来自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]
答案 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:)
。>
为什么要投射
编译器强迫您进行强制转换,因为它不能保证泛型类型LocaleQuery
为Array<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子句比当前更具体。