如何在Swift 4中编写NSFetchedResultsController的扩展

我正在尝试在Swift 4中为NSFetchedResultsController类编写一个简单的扩展。

这是我的第一次尝试 – 在Swift 3中有效:

 public extension NSFetchedResultsController { public func sectionCount() -> Int { if self.sections == nil { return 0 } return self.sections!.count } } 

但是我在Swift 4的Xcode 9 beta 2中得到了这个编译错误:

genericsObjective-C类的扩展无法在运行时访问类的generics参数

我尝试过其他变种无济于事。 请注意,我可以创建绑定到与resultType匹配的特定类型的NSManagedObject的扩展名; 但是这样做的缺点是我需要为NSFetchedResultsController使用的每个托管对象类型创建一个扩展。

最新的Swift 4文档似乎不能很好地解释这一点。

我想我找到了这个答案。 该function需要可用于Objective-C。

@objc添加到func应该删除编译错误。 至少它对我有用!

我们最终得到了子类(Swift 4)。 背后的想法 – 传递类型作为参数。 其余的安装程序运行NSManagedObject类型以解决编译错误。

 public class FetchedResultsController: NSFetchedResultsController { public convenience init(fetchRequest: NSFetchRequest, context: NSManagedObjectContext, _: T.Type) { self.init(fetchRequest: fetchRequest, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) } public var numberOfSections: Int { return sections?.count ?? 0 } public var numberOfFetchedEntities: Int { return fetchedObjects?.count ?? 0 } public func entity(at indexPath: IndexPath) -> T { if let value = object(at: indexPath) as? T { return value } else { fatalError() } } public func entity(at row: Int) -> T { let ip = IndexPath(item: row, section: 0) return entity(at: ip) } public var fetchedEntities: [T] { let result = (fetchedObjects ?? []).compactMap { $0 as? T } return result } } 

扩展:

 extension Requests { public static func all(_: T.Type) -> NSFetchRequest { let request = NSFetchRequest(entityName: T.entityName) return request } public static func ordered(by: String, ascending: Bool, _: T.Type) -> NSFetchRequest { let request = NSFetchRequest(entityName: T.entityName) request.sortDescriptors = [NSSortDescriptor(key: by, ascending: ascending)] return request } } extension NSManagedObject { public static var entityName: String { let className = NSStringFromClass(self) // As alternative can be used `self.description()` or `String(describing: self)` let entityName = className.components(separatedBy: ".").last! return entityName } public static var entityClassName: String { let className = NSStringFromClass(self) return className } } 

用法:

 extension ParentChildRelationshipsMainController { private func setupFetchedResultsController() -> FetchedResultsController { let request = DB.Requests.ordered(by: #keyPath(IssueList.title), ascending: true, IssueList.self) let result = FetchedResultsController(fetchRequest: request, context: PCDBStack.shared.mainContext, IssueList.self) return result } } extension ParentChildRelationshipsMainController: NSTableViewDataSource, NSTableViewDelegate { func numberOfRows(in tableView: NSTableView) -> Int { return frController.numberOfFetchedEntities } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let rowID = NSUserInterfaceItemIdentifier("rid:generic") let item = frController.entity(at: row) // ... } } 

子类化不好,组合是一种更好的方法。 以下是我对此的看法:

 public protocol FetchedDataProtocol { associatedtype T: NSFetchRequestResult var controller: NSFetchedResultsController { get } subscript(_ indexPath: IndexPath) -> T { get } } 

这样做可以避免强制转换和映射:

 public extension FetchedDataProtocol { subscript(_ indexPath: IndexPath) -> T { return controller.object(at: indexPath) } } 

然后你将它用作你自己的下标(以及你想要的)类:

 public struct MainFetchedData: FetchedDataProtocol { public let controller: NSFetchedResultsController public init(_ controller: NSFetchedResultsController) { self.controller = controller } }