用例
我有一个父类(super class) (FirebaseObject),其子类适用于我的 Firebase 中的大多数数据项(例如:RecipeItem、User)。我在父类(super class)中创建了一个自动更新子类中数据的函数,现在我正在尝试创建一个带有闭包的函数,该函数在对象更新时被调用。
代码
class FirebaseObject {
private var closures: [((FirebaseObject) -> Void)] = []
public func didChange(completion: @escaping (((FirebaseObject) -> Void))) {
// Save closures for future updates to object
closures.append(completion)
// Activate closure with the current object
completion(self)
}
//...
}
这会调用包含初始对象的闭包并将其保存以供以后更新。在我的 Firebase 观察器中,我现在可以在数据更新后通过调用激活所有闭包:
self.closures.forEach { $0(self) }
要添加这些监听对象更改的闭包,我需要执行以下操作:
let recipeObject = RecipeItem(data)
recipeObject.didChange { newFirebaseObject in
// Need to set Type even though recipeObject was already RecipeItem
// this will never fail
if let newRecipeObject = newFirebaseObject as? RecipeItem {
// Do something with newRecipeObject
}
}
问题
有没有办法让完成处理程序返回子类的类型,这样我就不必像那样做?子类
即使它永远不会失败?我尝试使用泛型类型来执行此操作,但我无法弄清楚,我不确定这是否是正确的解决方案。
我想将大部分代码保留在 FirebaseObject 类中,这样在创建新子类时就不需要添加很多代码。
编辑
基于 this article我尝试在创建子类时添加类型:
class RecipeItem: FirebaseObject<RecipeItem> {
//...
}
class FirebaseObject<ItemType> {
private var handlers: [((ItemType) -> Void)] = []
public func didChange(completion: @escaping (((ItemType) -> Void))) {
//...
这可以编译,但一旦初始化 RecipeItem 就会崩溃。我也试过了
class RecipeItem: FirebaseObject<RecipeItem.Type> {
//...
}
但是当我尝试访问 didChange
闭包中的 RecipeItem 数据时,这会产生一个有趣的编译器错误:
Instance member 'title' cannot be used on type 'RecipeItem'
最佳答案
好的,所以我已经为此工作了一天,并且我找到了使用 this answer 中的方法来完成它的方法。 didChange 和 initObserver 函数的灵感来自 this way of saving data in extensions .
首先,所有需要使用子类类型的函数都被移动到一个协议(protocol)中。
protocol FirebaseObjectType {}
extension FirebaseObjectType where Self: FirebaseObject {
private func initObserver(at ref: DatabaseReference) {
//...
}
mutating func didChange(completion: @escaping (((Self) -> Void))) {
if observer == nil {
// init Firebase observer here so there will be no Firebase
// observer running when you don't check for changes of the
// object, and so the Firebase call uses the type of whatever
// FirebaseObject this function is called on eg:
// RecipeItem.didChange returns RecipeItem
// and NOT:
// RecipeItem.didChange returns FirebaseObject
initObserver(at: ref)
}
if closureWrapper == nil {
// init closureWrapper here instead of in init() so it uses
// the class this function is called on instead of FirebaseObject
closureWrapper = ClosureWrapper<Self>()
}
// Save closure for future updates to object
closures.append(completion)
// Activate closure with current object
completion(self)
}
}
为了保存闭包,我现在使用包装类,这样我就可以对其进行类型检查。在 FirebaseObject 中:
class ClosureWrapper<T> {
var array: [((T) -> Void)]
init() {
array = []
}
}
fileprivate var closureWrapper: AnyObject?
现在我可以在 FirebaseObjectType 协议(protocol)中获得正确类型的闭包:
private var closures: [((Self) -> Void)] {
get {
let closureWrapper = self.closureWrapper as? ClosureWrapper<Self>
return closureWrapper?.array ?? []
}
set {
if let closureWrapper = closureWrapper as? ClosureWrapper<Self> {
closureWrapper.array = newValue
}
}
}
我现在可以在 FirebaseObject 子类上使用 didChange,而无需每次都检查其类型。
var recipe = RecipeItem(data)
recipe.didChange { newRecipe in
// Do something with newRecipe
}
关于swift - 子类类型作为闭包参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48077721/