USECASE
我有一个超类(FirebaseObject),其中包含Firebase中大多数数据项的子类(例如:RecipeItem,User)。我在超类中创建了一个自动更新子类中数据的函数,现在我正在尝试使用在更新对象时调用的闭包来创建一个函数。
代码
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
}
}
问题
有没有办法让完成处理程序返回子类的类型,所以我不必执行as? Subclass
即使它不会失败?我尝试用泛型类型做这个,但我无法弄清楚,我不确定这是否是正确的解决方案。
我想将大多数代码保存在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数据时,这会产生一个有趣的编译器错误:
实例成员'title'不能用于'RecipeItem'类型
答案 0 :(得分:0)
好的,所以我一直在研究这一天,我已经找到了一种方法,可以使用this answer中的方法为didChange和initObserver函数进行操作,并从this way of saving data in extensions中获取灵感。
首先,需要使用子类类型的所有函数都被移动到协议。
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协议中的正确类型获取闭包:
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
}