【问题标题】:findOrCreate in Swift 3 without hardcoding entityNameSwift 3 中的 findOrCreate 没有硬编码 entityName
【发布时间】:2017-03-30 13:54:19
【问题描述】:

Xcode 8 为您的 NSManagedObject 子类提供了自动生成的 extension,如下所示:

//  Animal+CoreDataProperties.swift

import Foundation
import CoreData

extension Animal {

  @nonobjc public class func fetchRequest() -> NSFetchRequest<Animal> {
    return NSFetchRequest<Animal>(entityName: "Animal");
  }

  // etc...
}

这很棒,因为我们不再需要硬编码字符串来创建NSFetchRequest 对象。但是,Apple 没有提供引用此方法的协议,也没有提供引用 entityName 本身的协议。

例如,如果我想创建这样的方法:

protocol ManagedObjectType: class {}

extension ManagedObjectType where Self: NSManagedObject {

  static func findOrCreate(inContext context: NSManagedObjectContext, matchingPredicate predicate: NSPredicate) -> Self {
    // etc...
  }
}

...我无法为任何NSManagedObject 子类利用fetchRequest 方法(因为它仅在子类上定义),所以我只剩下自己重新对这些字符串进行硬编码(即使 Xcode 已经这样做了)。

知道解决这个问题的方法吗?我当时在想一些事情:

protocol ManagedObjectType: class {
  static func fetchRequest() -> NSFetchRequest<NSFetchRequestResult>
}

extension Animal: ManagedObjectType {}

但这不起作用,因为协议不支持where子句,所以我不能指定SelfNSFetchRequestResult,这意味着当我来使用它时,像这样:

let request: NSFetchRequest<Self> = self.fetchRequest()

... Xcode 给我这个错误:

'fetchRequest' 产生 'NSFetchRequest',而不是 预期的上下文结果类型'NSFetchRequest'

有人解决过这个问题吗?

【问题讨论】:

    标签: ios swift core-data swift3 xcode8


    【解决方案1】:

    您可以为此目的使用模板。所以你的代码可以是这样的:

    static func findOrCreate<T: NSManagedObject>(inContext context: NSManagedObjectContext, matchingPredicate predicate: NSPredicate) -> T {
        // etc...
    }
    

    【讨论】:

    • 这实际上不起作用,因为我仍然无法在T 上致电fetchRequest。但是,我赞成,因为您的回答帮助我找到了解决方案(见上文)。
    • 您可以将其扩展为 NSManagedObject ,然后像 Animal.findOrCreate(...) 一样使用它,这将与您所取得的成就完全相同。纠正我,如果我错了
    • 你是对的 - 你可以这样做,但这有点误导。 findOrCreate 仍将使用泛型返回适当类型的东西;在Animal 类上调用该方法的事实是无关紧要的。例如,这可以正常工作:let fruit: Fruit = Animal.findOrCreate(...)(即使 FruitAnimal 根本不相关)。
    【解决方案2】:

    最后,一种稍微不同的方法对我有用。

    首先,定义一个使用associatedtypeprotocol - 这是必需的,因为您不能在protocol 中将Self 作为泛型类型引用,除非该类被标记为final(即它不是在自动生成的NSManagedObject 子类的情况下):

    protocol ManagedObjectType: NSFetchRequestResult {
    
      associatedtype ResultType: NSFetchRequestResult
    
      init(context: NSManagedObjectContext)
      static func fetchRequest() -> NSFetchRequest<ResultType>
    
    }
    

    您需要在此处添加您需要在帮助方法中访问的任何 NSManagedObject 属性和方法。就我而言,到目前为止,这只是 init(context:) 初始化程序。

    这使得将您的 NSManagedObject 子类标记为符合要求稍微复杂一些:

    extension Animal: ManagedObjectType {
      typealias ResultType = Animal
    }
    

    现在您可以像这样提供辅助方法(注意where 子句):

    static func findOrCreate<T: ManagedObjectType>(inContext context: NSManagedObjectContext, matchingPredicate predicate: NSPredicate, configure: (T) -> Void) -> T where T.ResultType == T {
    
      // You are now able to call fetchRequest like this:
      let request: NSFetchRequest<T> = T.fetchRequest()
    
      // etc...
    }
    

    由于不再需要在NSManagedObject 本身上定义辅助方法,因此您可以将它们放在任何地方。我在帮助器struct 中创建了static 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 2014-03-27
      • 1970-01-01
      • 2021-04-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多