仅使用协议和协议扩展在 Swift 中生成自动递增的实例 ID
Generating auto-incrementing Instance IDs in Swift using Protocols and protocol-extensions only
目标
创建具有以下行为的“AutoIDable”协议。
- 每个符合此协议的 class 实例都将获得一个自动生成的字符串类型的“id”属性。
- 代码应生成
<prefix><Instance-count-starting-from-1>
格式的 ID 字符串(例如:E-1
、E-2
、...E-<n>
等 1st 和 2nd。 .. 符合条件的第 n 个实例 class.
- 协议和协议扩展应该做 ALL 所需的工作来生成 id 字符串。符合要求的 class 只需订阅协议,仅此而已。
当前状态:
我通过以下实施实现了目标 1 和目标 2:
protocol Identifiable {
var id: String { get }
}
protocol AutoIDable: Identifiable{
static var _instanceCount: Int { get set }
}
class AutoID: AutoIDable {
init(idPrefix: String) {
setAutoID(prefix: idPrefix)
}
internal static var _instanceCount: Int = 0
var id: String = ""
func setAutoID(prefix: String = ""){
Self._instanceCount += 1
self.id = "\(prefix)\(Self._instanceCount)"
}
}
class Employee: AutoID {
init(){
super.init(idPrefix: "E-")
}
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id)
print(e2.id)
print(e3.id)
print(e1.id)
以上代码运行的输出:
E-1
E-2
E-3
E-1
待办事项:
为了实现目标 3,我需要消除 AutoID
superclass 并使用协议扩展实现相同的功能。
我 运行 陷入麻烦是因为:
- 协议扩展不允许静态存储属性。我确实知道如何在不使用 superclass.
的情况下解决此限制
- 我不知道如何将代码注入到 Employee class 的创建者可能创建的所有初始化程序中。同样,如果不使用 superclass.
我想不出解决方法
如果你能指出正确的方向,我将不胜感激。
PS:Swift 编程新手。如果您有以更“快速”的方式实施代码的建议,请告诉我。 :-)
既然要使用协议,协议中不能有存储的属性。因此,如果不是 ID 本身,您将需要一些地方来存储递增的 ID 值。
不确定它是否违反了您仅使用协议的要求,因为它需要一种存储类型,但至少它不需要符合 classes 来拥有一个超级class .
所以,假设我们构建了这样一个 class 来保存所有 ID 并保持递增计数器:
class AutoIncrementId {
static private var inc: Int = 0
static private var ids: [ObjectIdentifier: String] = [:]
static func getId(_ objectId: ObjectIdentifier, prefix: String) -> String {
if let id = ids[objectId] { return id }
else {
inc += 1
let id = "\(prefix)\(inc)"
ids[objectId] = id
return id
}
}
}
那么协议要求可以是:
protocol AutoIdentifiable {
static var prefix: String { get }
var id: String { get }
}
因此,class 需要定义其前缀。但是我们可以为 id
:
定义默认实现
extension AutoIdentifiable where Self: AnyObject {
var id: String {
AutoIncrementId.getId(ObjectIdentifier(self), prefix: Self.prefix)
}
}
用法为:
class Employee: AutoIdentifiable {
static let prefix = "E-"
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id) // E-1
print(e2.id) // E-2
print(e3.id) // E-3
print(e1.id) // E-1
目标
创建具有以下行为的“AutoIDable”协议。
- 每个符合此协议的 class 实例都将获得一个自动生成的字符串类型的“id”属性。
- 代码应生成
<prefix><Instance-count-starting-from-1>
格式的 ID 字符串(例如:E-1
、E-2
、...E-<n>
等 1st 和 2nd。 .. 符合条件的第 n 个实例 class. - 协议和协议扩展应该做 ALL 所需的工作来生成 id 字符串。符合要求的 class 只需订阅协议,仅此而已。
当前状态:
我通过以下实施实现了目标 1 和目标 2:
protocol Identifiable {
var id: String { get }
}
protocol AutoIDable: Identifiable{
static var _instanceCount: Int { get set }
}
class AutoID: AutoIDable {
init(idPrefix: String) {
setAutoID(prefix: idPrefix)
}
internal static var _instanceCount: Int = 0
var id: String = ""
func setAutoID(prefix: String = ""){
Self._instanceCount += 1
self.id = "\(prefix)\(Self._instanceCount)"
}
}
class Employee: AutoID {
init(){
super.init(idPrefix: "E-")
}
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id)
print(e2.id)
print(e3.id)
print(e1.id)
以上代码运行的输出:
E-1
E-2
E-3
E-1
待办事项:
为了实现目标 3,我需要消除 AutoID
superclass 并使用协议扩展实现相同的功能。
我 运行 陷入麻烦是因为:
- 协议扩展不允许静态存储属性。我确实知道如何在不使用 superclass. 的情况下解决此限制
- 我不知道如何将代码注入到 Employee class 的创建者可能创建的所有初始化程序中。同样,如果不使用 superclass. 我想不出解决方法
如果你能指出正确的方向,我将不胜感激。
PS:Swift 编程新手。如果您有以更“快速”的方式实施代码的建议,请告诉我。 :-)
既然要使用协议,协议中不能有存储的属性。因此,如果不是 ID 本身,您将需要一些地方来存储递增的 ID 值。
不确定它是否违反了您仅使用协议的要求,因为它需要一种存储类型,但至少它不需要符合 classes 来拥有一个超级class .
所以,假设我们构建了这样一个 class 来保存所有 ID 并保持递增计数器:
class AutoIncrementId {
static private var inc: Int = 0
static private var ids: [ObjectIdentifier: String] = [:]
static func getId(_ objectId: ObjectIdentifier, prefix: String) -> String {
if let id = ids[objectId] { return id }
else {
inc += 1
let id = "\(prefix)\(inc)"
ids[objectId] = id
return id
}
}
}
那么协议要求可以是:
protocol AutoIdentifiable {
static var prefix: String { get }
var id: String { get }
}
因此,class 需要定义其前缀。但是我们可以为 id
:
extension AutoIdentifiable where Self: AnyObject {
var id: String {
AutoIncrementId.getId(ObjectIdentifier(self), prefix: Self.prefix)
}
}
用法为:
class Employee: AutoIdentifiable {
static let prefix = "E-"
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id) // E-1
print(e2.id) // E-2
print(e3.id) // E-3
print(e1.id) // E-1