swift - 子类类型作为闭包参数

标签 swift firebase generics

用例

我有一个父类(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/

相关文章:

ios - 如何在 Swift2 中以编程方式设置约束?

android - Firebase 任务尚未完成

ios - Firebase 重置密码 Swift

ios - 在 viewWillAppear 中设置约束

ios - 解析推送通知添加新操作

ios - Localizable.strings 不翻译短语

firebase - 如何检查 Firebase 实时数据库(Dart/Flutter)中是否存在值?

c# - 从继承具体类对抽象类的类型推断

JavaFX 泛型在自定义 Consumer 和每个 Consumer 所需的类型之间不兼容

c# - `System.Type` 如何成为具有约束的通用参数?