我在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;这是完全有道理的。
但我不确定另一种方法可以做我想做的事情。
答案 0 :(得分:4)
这似乎是......的工作。
有很多关于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)
这至少可以编译。