Swift 3中的多个NSFetchedResultsControllers

时间:2016-11-01 17:17:08

标签: swift generics core-data swift3

我在Swift中有一个包装多个NSFetchedResultsControllers的类,成为它们的委托,并在返回它自己的委托之前转换IndexPaths。只要这些类符合相同的协议,该类就可以使NSFetchedResultsControllers返回不同的实体。升级到Swift 3时,我无法获得相同的编译功能。

假设我想要包装两个NSFetchedResultsControllers,返回两个不同的Entity类型,以便在单个tableView中显示。两个CoreData实体都符合以下协议

protocol ManagedObjectDisplayType : NSFetchRequestResult {
    var id:String { get }
    func friendlyName() -> String
}

问题是现在NSFetchedResultsControllers是通用的,因为两个控制器的类型不同,所以我没有传递给我的Wrapper类的具体类型的NSFetchedResultsController。

例如:

 let entity1Request = NSFetchRequest<Entity1>(entityName: entityName)
 let entity1Frc = NSFetchedResultsController<ManagedObjectDisplayType>(fetchRequest: entity1Request, managedObjectContext:mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
 let entity2Request = NSFetchRequest<Entity2>(entityName: entityName)
 let entity2Frc = NSFetchedResultsController<ManagedObjectDisplayType>(fetchRequest: entity2Request, managedObjectContext:mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)

当我这样做时,我收到以下错误:&#34;使用&#39; ManagedObjectDisplayType&#39;作为符合协议的具体类型&NSFetchRequestResult&#39;不受支持&#34;这是完全有道理的。

但我不确定另一种方法可以做我想做的事情。

2 个答案:

答案 0 :(得分:4)

这似乎是......的工作。

enter image description here

类型擦除!

有很多关于Swift中类型擦除的文章,但这是一个有趣的情况。我假设您在创建一个可以为您的不同类型保存多个NSFetchedResultsController的对象时遇到问题。问题是Swift(在其当前版本中)需要一个具体类型来正确分配所需的内存。标准库和其他地方的标准解决方案是创建一个隐藏基础类型的框。在这种情况下,下面的AnyFetchedResultsController类有效地删除它所包含的具体类型(NSFetchedResultsController<T> where T: ManagedObjectDisplayType)。

class AnyFetchedResultsController: CustomDebugStringConvertible
{
    var descImpl: () -> String
    var performImpl: () throws -> ()

    init<T>(_ controller: NSFetchedResultsController<T>) where T: ManagedObjectDisplayType {    
        descImpl = { controller.debugDescription }
        performImpl = { try controller.performFetch() }
    }

    func performFetch() throws {
        try performImpl()
    }

    var debugDescription: String {
        return "wrapping \(descImpl())"
    }
}

let entity1Request = NSFetchRequest<Entity1>(entityName: "Foobar")
let entity1Frc = NSFetchedResultsController<Entity1>(fetchRequest: entity1Request, managedObjectContext:mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
let entity2Request = NSFetchRequest<Entity2>(entityName: "Barfoo")
let entity2Frc = NSFetchedResultsController<Entity2>(fetchRequest: entity2Request, managedObjectContext:mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)


let frcs: [AnyFetchedResultsController] = [AnyFetchedResultsController(entity1Frc), AnyFetchedResultsController(entity2Frc)]

现在您已经有了存储这些获取结果控制器的方法,您需要使用您需要在基础AnyFetchedResultsController上调用的任何其他方法来充实NSFetchedResultsController类。

我希望这是有道理的。如果您还有其他问题,请回来!

修改

AnyFetchedResultsController.init的原始签名:

init<T, U: NSFetchedResultsController<T>>(_ controller: U, _ managedObjectType: T? = nil) where T: ManagedObjectDisplayType

非常复杂,并且有一个虚拟的managedObjectType参数,似乎是修复某些编译器错误所必需的。但是,我刚发现更简单:

init<T>(_ controller: NSFetchedResultsController<T>) where T: ManagedObjectDisplayType

(也在上面)似乎同样有效。

答案 1 :(得分:2)

使用实际的具体类而不是ManagedObjectDisplayType

我假设你有一个像这样的Entity1声明:

class Entity1 : NSManagedObject, ManagedObjectDisplayType {
    var id:String {
        return "42"
    }

    func friendlyName() -> String {
        return "foobar"
    }

}

然后代替

 let entity1Request = NSFetchRequest<Entity1>(entityName: entityName)
 let entity1Frc = NSFetchedResultsController<ManagedObjectDisplayType>(fetchRequest: entity1Request, managedObjectContext:mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)

你会有

 let entity1Request = NSFetchRequest<Entity1>(entityName: entityName)
 let entity1Frc = NSFetchedResultsController<Entity1>(fetchRequest: entity1Request, managedObjectContext:mainManagedObjectContext, sectionNameKeyPath: nil, cacheName: nil)

这至少可以编译。