3

Document根据有关文档名称的一些查询,我有兴趣编写一个返回 class 对象的方法。

Document发生在 subclass NSManagedObject,但消费者并不真正关心这一点。对象在Document方法内部成功出错,并且其属性是可访问的。但是,一旦离开该方法并NSManagedObjectContext释放用于获取的方法,该对象将再次变为故障。

有没有办法NSManagedObject从它的上下文中分离出来,所以它不会变成一个错误?当然这不允许保存对对象的更改,但无论如何我对此不感兴趣。

我不想诉诸任何一个:

  1. 编写一个NonManagedDocument反映班级的Document班级。
  2. NSManagedObjectContext只要我对文档感兴趣,就可以标记。消费者不在乎,也不必知道Document子类NSManagedObject.

笔记:

我要补充一点,即使我能够保留sDocument之后NSManagedObjectContext,这也不是一个很好的解决方案,因为消费者可以访问Documents 接口作为NSManagedObjectContext子类,这是不可取的,因为它可能会导致意外的错误。但是,为每个碰巧存储在数据库中的模型对象手动编写冻干包装器的替代方法非常没有吸引力。

4

6 回答 6

3

我真的不明白你的问题。如果你公开了一个继承自 NSManagedObject 的类,那么该类的用户就会有这个强制,以及伴随的问题。

如果您将您的类公开为一个组合,则托管对象的详细信息就在该类中,并且这些详细信息很容易被隐藏。

您当然可以根据需要创建/销毁 MOC,它们非常便宜。但是,这似乎并不是超级有益的。

我建议保留一个用于界面的托管对象上下文。您提到担心多个线程调用到 MOC。这是一个有效的问题,但通过使用带有 NSPrivateQueueConcurrencyType 的 MOC 可以轻松有效地克服。

然后,每当您需要获取对象时,您只需执行

[moc performBlockAndWait:^{
    // Fetch the object from the MOC
}];

调用同步版本时要小心,因为您有可能出现死锁。

或者,您仍然可以使用为每次提取创建唯一上下文的选项...

NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
moc.parentContext = myPrivateQueueContext;
// Now, you can fetch from this MOC, and do what you want with the object
// and when you are done, you just let the MOC dealloc.

您可能想查看 NSFetchRequest 的选项,以及 MOC 本身的设置,特别是

refreshObject:mergeChanges:
retainsRegisteredObjects

如果您告诉 myPrivateQueueContext 保留对象,它会将它们保留在其本地 MOC 中。然后,当您执行 fetch 时,您可以告诉 fetch 更喜欢 MOC 中的缓存版本,它甚至永远不会进入磁盘。

如果您存储托管对象的 objectID,您可以获得更多优势,并且只需调用 objectRegisteredForID: 即可查找已注册的对象。无论谁否决了答案,然后您可以手动清理缓存,因此如果需要,它不会变得太大。

或者,您可以调用 existingObjectWithID:error: 永远不会返回错误。结果将始终是已实现的对象或 nil。

无论如何,我认为您想做的事情可以通过多种方式轻松解决。不过,我会提醒您,不要过度设计您的解决方案。

找出你想要公开的接口,并实现它。如果您以后发现性能问题,请在那时处理它们。

** 编辑 **

专门针对 Danra 的评论(以及对答案投反对票的人)。我从未打算让您直接从私人 MOC 访问数据。如果要访问私有 MOC,请使用 performBlock。

但是,您可以根据需要创建任意数量的子...任何并发类型,并根据您的意愿使用它们。当他们需要与数据库对话时,他们将直接与父级对话(在适当的上下文中)。

我所说的一切都没有建议您对私有 MOC 做任何事情,但调用 performBlock 或使其成为另一个 MOC 的父级。

于 2012-07-29T21:39:33.073 回答
3

NSManagedObjects 与核心数据堆栈紧密绑定。实体不一定完全加载到内存中,关系也肯定不是。好吧,据我所知,没有支持的方式来做你要求的事情。

您很好地描述了您拥有的替代方案。很抱歉听到你讨厌他们。;)

也就是说,可以在运行时生成镜像类,您不必手动编写和维护每个实体。ObjC 运行时允许我们做一些花哨的事情。


编辑:一些关于 NSDictionary 快照的片段

NSManagedObject* any; // let's imagine you have an instance at hand
NSAttributeDescription* entityDescription = any.entity;

// Attributes
NSDictionary* attributeDescriptions = entityDescription.attributesByName;
for ( NSAttributeDescription* attributeDescription in attributeDescriptions.allValues )
{
    NSString* attributeName = attributeDescription.name;
    NSAttributeType attributeType = attributeDescription.attributeType;
    Class attributeClass = NSClassFromString( attributeDescription.attributeValueClassName );

    …
    id attributePrimeValue = [any primitiveValueForKey: attributeName];
    id attributeValue = [any valueForKey: attributeName];
    …

}

// Relationships
NSDictionary* relationshipDescriptions = entityDescription.relationshipsByName;
for ( NSRelationshipDescription* relationshipDescription in relationshipDescriptions.allValues )
{
    …
}

以此为起点,您应该能够遍历属性,获取它们的名称、类型和值,并创建基于 NSDictionary 的快照。关系的迭代是相同的,但当然,您将对关系执行的操作与您的需求紧密相关。

于 2012-07-21T08:54:56.443 回答
2

为什么不直接隐藏NSManagedObjectContext?您的消费者可能不关心 is 是 a 的事实NSManagedObject,但他们不需要使用NSManagedObjectContext,这将是您的实现细节。您永远不需要公开NSManagedObjectContext和只为所有文档使用一个。

于 2012-07-26T14:12:02.653 回答
2

我不确定您为什么要释放托管对象上下文。为什么不在应用程序委托中设置核心数据堆栈,以便您可以从程序中的任何位置访问您的 NSManagedObjects?

如果您需要执行后台任务,请确保在主线程(在应用程序委托中)创建一个 NSManagedObjectContext 并在后台线程中创建另一个 NSManagedObjectContext。

您可能需要查看 Magical Record (https://github.com/magicalpanda/MagicalRecord/) 以帮助大大简化使用 Core Data 并正确管理多个线程上的上下文。

于 2012-07-26T14:35:55.267 回答
2

以@hypercrypt 的回答为基础:为什么不公开您的 API 基于托管对象的事实?这类似于@jody 的建议。

您可以使用 NSManagedObjectContext 上的类别来处理获取 per-thread NSManagedObjectContext。就像是:

@implementation NSManagedObjectContext (PerThreadContext)

+(NSManagedObjectContext*)contextForCurrentThread
{
    static OSSpinLock lock = OS_SPINLOCK_INIT ;
    @try 
    {
        OSSpinLockLock( & lock ) ;

        NSDictionary * info = [ NSThread currentThread ].threadDictionary ;
        NSManagedObjectContext * context = [ info valueForKey:@"managedObjectContext" ] ;
        if ( !context )
        {
            context = [ [ NSManagedObjectContext alloc ] initWithConcurrencyType:NSConfinementConcurrencyType ] ;
            context.persistentStoreCoordinator = [ NSPersistentStoreCoordinator sharedCoordinator ] ; // perhaps a category we added to NSPersistentStoreCoordinator? Or use your app delegate, etc...
            [ info setObject:context forKey:@"managedObjectContext" ] ;
        }
    }
    @finally 
    {
        OSSpinLockUnlock( & lock ) ;
    }
    return context ;
}

@end

当您的 API 方法之一被调用时:

-(NSManagedObject*)getSomeObject
{
    NSManagedObjectContext * context = [ NSManagedObjectContext contextForCurrentThread ] ;

    NSManagedObject * result = ....code...
    return result ;
}

现在,如果您想强制不变性,您可以向 NSManagedObject 添加一个类别(或为您的对象图实例创建一个基类)

作为一个类别:

@interface NSManagedObject (Freezing)
@property ( nonatomic, readonly ) BOOL frozen ;
-(void)freeze ;
@end

@implementation NSManagedObject (Freezing)

-(void)setFrozen:(BOOL)f
{
    objc_setAssociatedObject( self, "_frozen", [ NSNumber numberWithBool:f ], OBJC_ASSOCIATION_RETAIN_NONATOMIC ) ;
}

-(BOOL)frozen
{
   return [ objc_getAssociatedObject( self, "_frozen" ) boolValue ] ;
}

-(void)freeze
{
    [ self setFrozen:YES ] ;
}

-(BOOL)validateForUpdate:(NSError**)error
{
    if ( self.frozen ) 
    { 
        if ( error ) { *error = [ NSError ... ] ; }
        return NO ; 
    }

    return [ super validateForUpdate:error ] ;
}

-(BOOL)validateForDelete:(NSError**)error
{
    if ( self.frozen ) 
    { 
        if ( error ) { *error = [ NSError ... ] ; }
        return NO ; 
    }

    return [ super validateForDelete:error ] ;
}

@end

最后,如果您在拆除核心数据堆栈时已经下定决心,我会-(NSDictionary*)dictionaryRepresentation在 NSManagedObject 中添加一个类别,以将您的托管对象转换为您传递给消费者的字典。(你可以看到我喜欢类别?)

对于从核心数据提取返回的NSDictionaryResultType结果,请在您的提取请求中使用结果类型。这会将结果作为字典返回。

高温高压

于 2012-07-31T20:00:21.993 回答
2

你不能——NSManagedObjects总是在核心数据堆栈中保持绑定到他们的上下文。这是有道理的。

一种可能的解决方案是编写类似于mogenerator的东西,但它会自动创建非托管快照类,以及创建这些副本的托管类上的方法。

或者,可能有一些 obj-C 技巧来生成NSManagedObjects保留其上下文的子类,隐藏它们子类化的事实NSManagedObject,因此消费者只暴露于快照属性而不暴露于整个NSManagedObject功能。

于 2012-08-01T17:51:37.063 回答