Swift 3中的多个NSFetchedResultsControllers

Nat*_*art 5 generics core-data swift swift3

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

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

protocol ManagedObjectDisplayType : NSFetchRequestResult {
    var id:String { get }
    func friendlyName() -> String
}
Run Code Online (Sandbox Code Playgroud)

问题在于,现在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)
Run Code Online (Sandbox Code Playgroud)

当我这样做时,出现以下错误:“不支持将'ManagedObjectDisplayType'作为符合协议'NSFetchRequestResult'的具体类型使用”,这是很有意义的。

但是我不确定要做什么。

Dav*_*ton 5

这看起来像是...的工作

在此处输入图片说明

输入擦除!

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

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)]
Run Code Online (Sandbox Code Playgroud)

现在您已经有了存储这些获取的结果控制器的方法,您将需要AnyFetchedResultsController使用需要在底层调用的其他方法来充实该类NSFetchedResultsController

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

编辑

原始签名AnyFetchedResultsController.init

init<T, U: NSFetchedResultsController<T>>(_ controller: U, _ managedObjectType: T? = nil) where T: ManagedObjectDisplayType
Run Code Online (Sandbox Code Playgroud)

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

init<T>(_ controller: NSFetchedResultsController<T>) where T: ManagedObjectDisplayType
Run Code Online (Sandbox Code Playgroud)

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