推荐使用CoreData托管对象执行依赖注入的方法?

时间:2017-04-03 14:56:36

标签: ios swift core-data

鉴于我有一个由CoreData堆栈管理的类, 我目前正在编写一个Framework,我希望能够通过依赖注入创建我的对象(理想情况下,只能通过DI)。

我也在设计"快速失败"心态(即尽快崩溃),并遵循SOLID原则。

该框架将与iOS 9-10兼容,并且可以在ObjC和Swift(或混合目标)中使用,因此我理想地不希望依赖于ObjC或Swift中的非交叉兼容功能。

这段代码意在内部使用,而不是暴露给框架的消费者,因为Jonah指出暴露这些功能并不是一个非常可靠的选择。

问题:

我有2种不同的方法来创建我的对象,我不确定是否 如果有一个"首选"它们中的任何一个都是纠正的。解决方案,或使用较少陷阱的解决方案?

这是一个代码片段来说明我的情况:

import CoreData
class Example: NSManagedObject {}

class DependencyClass: NSObject {}

extension Example
{
    public convenience init(with someDependency:DependencyClass,
                            context:NSManagedObjectContext)
    {
        let description = NSEntityDescription.entity(forEntityName: "Example", in: context)
        self.init(entity: description!, insertInto: context)
        //Configure the result object here
    }

    class func NotAConvenienceInit(with someDependency:DependencyClass,
                       context:NSManagedObjectContext) -> Example
    {
        let selfClassName = String(describing: self)
        let result = NSEntityDescription.insertNewObject(forEntityName: selfClassName, into: context) as! Example
        //Configure the result object here
        return result
    }
}

let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
let dependency = DependencyClass()

//Usecase A
let myExample = Example(with: dependency, context: context)

//Usecase B
let anotherExample = Example.NotAConvenienceInit(with: dependency, context: context)

我一直在考虑为方便init提供可用的初始化程序,我很好地认为,一旦init方法结束,我就会保证"我的对象是有效的。我认为两种方式都可以通过各种机制来保证。

Apple似乎更喜欢their documentation中的+insertNewObjectForEntityForName:inManagedObjectContext:,这就是我提出这两种方法的原因。

PS:我还在学习Swift,如果这似乎是一个已经解决的问题,我提前道歉。在各种网站上阅读了很多文章后,我没有找到一个确凿的解决方案,因此我转向了SO。

1 个答案:

答案 0 :(得分:2)

我试图做同样的事情,我的结论是,最好避免这种方法,而是保持行为,特别是需要其他依赖关系的行为。

为什么?

  1. Swift和Objective-C真的没有给你任何工具来禁止使用现有的构造函数,所以当你可以提供DI参数的替代方案时,你几乎无法确保NSManagedObjectNSEntityDescription不用于创建模型的实例。

  2. 此外,NSManagedObject.init(entity:insertInto:)NSEntityDescription.insert​New​Object(for​Entity​Name:​into:​)用于插入新模型实例,但不是创建模型类实例的唯一方法。请查看NSManagedObject的{​​{1}}和awake​From​Fetch()(以及awake(from​Snapshot​Events:​))。

    假设我创建了一个带有获取请求的awake​From​Insert(),返回一个框架模型。如果我打电话给NSFetchedResultsController,我会得到你模特的实例。同样,我可以直接执行controller.fetchedObjects。您可以使用单例容器在NSFetchRequest实现中提供依赖项,但这会限制创建代码指定这些依赖项的能力并导致另一个问题:

  3. 托管对象上下文队列。由于awake...与内容绑定并且必须在该上下文的队列中使用,因此您需要注意在错误队列上引入对模型的访问的依赖关系,并且通常希望将依赖关系作用于特定上下文。

    < / LI>

    相反,我会考虑:

    • 让您的框架返回非NSManagedObject模型,这些模型是您的Core Data存储中的状态快照(可能是不可变的)。如果您可以避免将Core Data的使用暴露给框架的使用者,那么您可以为他们提供不受特定并发队列限制的模型,并且您可以完全拥有这些模型的生命周期。
    • 避免模型中的任何外部依赖关系,并将此类行为移动到服务类或其他接口中,然后可以更轻松地使用DI。确保这些服务类尊重它们所给出的任何模型的并发队列,因此所有接口可能都需要是异步的。