Swift: 在协议扩展的静态变量中存储协议的实现类型
Swift: Store Type of implementation of Protocol in static var of protocol extension
好的,我有这个协议 MenuEntry
,我想用它来填充 TableView:
protocol MenuEntry {
static var title: String { get }
static func entrySelected(_ menuController: MenuController)
}
我想在各个地方实现这个协议,让项目自己决定做什么。它可能是一个实现协议的 UIViewController 或一个简单的结构,然后调用菜单本身的函数:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
现在我想构建 MenuControllers 数据源但不实际实例化条目,因为特别是当填充 MenuControllers 数据源时我的视图控制器不一定可用。这就是我在 MenuEntry 中使用 static var/func 的原因。目前,我可以简单地这样做来填充数据源:
let dataSource: [MenuEntry.Type] = [SomeEntry.self]
它似乎工作得很好。我可以获取条目并调用相应的函数:
dataSource.first?.title //Gives me "Some Entry"
棘手的部分来了。我认为我可以非常聪明地创建一个协议扩展,在其中引用我实现协议的所有类型,如下所示:
extension MenuEntry {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
//...
}
然后通过 MenuEntry.someEntry
使用它们。但是,在 MenuEntry 上访问 someEntry 给我一个错误:
error: static member 'someEntry' cannot be used on protocol metatype 'MenuEntry.Protocol'
所以我的问题是:我错过了什么?我只是想以一种非预期的方式滥用语言,还是我只是做错了什么?
解决方案
根据下面接受的答案,这是我现在做事的方式。首先,我们需要提到的结构(我猜不需要 class):
struct MenuEntries {}
然后,在我实现 MenuEntry 协议的地方,我也扩展了这个结构并像这样添加条目:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
extension MenuEntries {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
}
最后一件事是像这样创建我的数据源:
let dataSource: [MenuEntry.Type] = [MenuEntries.someEntry, ...]
好的,现在我在一个地方有了所有菜单条目的列表。缺点是我必须记住每次都要扩展 MenuEntries。除了有一些神奇的方法可以在我不知道的条件基础上扩展结构。但我想这有点过头了,根本不可能。
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements.”
您的扩展试图直接在协议中实现功能,但这是不允许的;只有采用该协议的class、结构或枚举才能提供功能。
您可以定义一个 class,returns 您的菜单 classes:
class MenuFactory {
static var someEntry: MenuEntry.type { return SomeEntry.self }
}
好的,我有这个协议 MenuEntry
,我想用它来填充 TableView:
protocol MenuEntry {
static var title: String { get }
static func entrySelected(_ menuController: MenuController)
}
我想在各个地方实现这个协议,让项目自己决定做什么。它可能是一个实现协议的 UIViewController 或一个简单的结构,然后调用菜单本身的函数:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
现在我想构建 MenuControllers 数据源但不实际实例化条目,因为特别是当填充 MenuControllers 数据源时我的视图控制器不一定可用。这就是我在 MenuEntry 中使用 static var/func 的原因。目前,我可以简单地这样做来填充数据源:
let dataSource: [MenuEntry.Type] = [SomeEntry.self]
它似乎工作得很好。我可以获取条目并调用相应的函数:
dataSource.first?.title //Gives me "Some Entry"
棘手的部分来了。我认为我可以非常聪明地创建一个协议扩展,在其中引用我实现协议的所有类型,如下所示:
extension MenuEntry {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
//...
}
然后通过 MenuEntry.someEntry
使用它们。但是,在 MenuEntry 上访问 someEntry 给我一个错误:
error: static member 'someEntry' cannot be used on protocol metatype 'MenuEntry.Protocol'
所以我的问题是:我错过了什么?我只是想以一种非预期的方式滥用语言,还是我只是做错了什么?
解决方案
根据下面接受的答案,这是我现在做事的方式。首先,我们需要提到的结构(我猜不需要 class):
struct MenuEntries {}
然后,在我实现 MenuEntry 协议的地方,我也扩展了这个结构并像这样添加条目:
struct SomeEntry: MenuEntry {
static var title: String { return "Some Entry" }
static func entrySelected(_ menuController: MenuController) {
menuController.doSomething()
}
}
extension MenuEntries {
static var someEntry: MenuEntry.Type { return SomeEntry.self }
}
最后一件事是像这样创建我的数据源:
let dataSource: [MenuEntry.Type] = [MenuEntries.someEntry, ...]
好的,现在我在一个地方有了所有菜单条目的列表。缺点是我必须记住每次都要扩展 MenuEntries。除了有一些神奇的方法可以在我不知道的条件基础上扩展结构。但我想这有点过头了,根本不可能。
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements.”
您的扩展试图直接在协议中实现功能,但这是不允许的;只有采用该协议的class、结构或枚举才能提供功能。
您可以定义一个 class,returns 您的菜单 classes:
class MenuFactory {
static var someEntry: MenuEntry.type { return SomeEntry.self }
}