CoreData 瞬态关系示例

Posted

技术标签:

【中文标题】CoreData 瞬态关系示例【英文标题】:CoreData transient relationship example 【发布时间】:2013-04-23 05:27:51 【问题描述】:

有人有关于如何在 CoreData 中对 transient 对一 relationship 进行建模和编码的示例吗?例如,我有 2 个具有一对多关系的实体。医生和预约。现在我想在医生实体上建立一个名为 mostRecentAppointment 的临时关系。在 xcode 设计器中建模很简单,但我不确定实现方面。我还应该实施逆向吗?看起来很傻。

【问题讨论】:

很确定不支持开箱即用的瞬态关系,您必须手动合并一个。这可能无法直接回答您的问题,但实现mostRecentAppointment 关系的最简单方法是在您的数据访问层类中创建一个方法,并使用带有日期描述谓词的NSFetchRequest 检索它 嗯。设计器确实允许您将关系标记为瞬态。所以那里似乎有什么东西。我希望利用它附带的 KVO 和缓存,因为我不想每次访问对象时都执行 fetchrequest。希望有类似正常瞬态属性的东西。 嗨。你解决问题了吗?我正在解决[一个相关问题](***.com/questions/42812151/…) 似乎暂时的关系没有按预期工作。 【参考方案1】:

看看我最近写的这段代码,将图像缓存在 NSManagedObject 中:

首先,您在模型中定义一个 transient 属性(请注意,如果您的瞬态属性指向 CoreData 支持的对象类型以外的对象类型,您将在模型中保留为“Undefined” )

然后,您为该实体重新生成 NSManagedObject 子类或手动添加新属性,头文件应如下所示:

@interface Card : NSManagedObject

@property (nonatomic, retain) NSString * imagePath;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSNumber * order;
@property (nonatomic, retain) NSString * displayName;
@property (nonatomic, retain) UIImage *displayImage;

@end

这里我们将transient属性的class改为实际的class类型

例如displayImage 这里的输入是UIImage

在实现文件(或扩展类)中,您为瞬态属性实现 getter/setter:

-(UIImage*)displayImage

  //Get Value
  [self willAccessValueForKey:@"displayImage"];
  UIImage *img = (UIImage*)[self primitiveValueForKey:@"displayImage"];
  [self didAccessValueForKey:@"displayImage"];

   if (img == nil) 
    if ([self imagePath])  //That is a non-transient property on the object
      img = [UIImage imageWithContentsOfFile:self.imagePath];
      //Set Value
      [self setPrimitiveValue:img forKey:@"displayImage"];
    
  
  return img;

希望对你有所帮助。

【讨论】:

我知道如何做到这一点,它比你说的要复杂一些,因为你需要处理缓存失效和故障。不管我的问题是关于短暂的关系而不是属性。几乎没有关于短暂关系的信息。因此我发布了这个问题。例如,当事物被添加或删除到正常的一对多关系(添加正常约会)时,如何处理缓存失效。【参考方案2】:

您需要做的是添加一个名为 newAppointment 的约会类型实体,并在每次为给定医生创建新约会时设置它。就这么简单。

始终按照苹果的建议实施逆向验证和核心数据效率。

或者,您可以为约会添加时间戳,并使用 NSPredicates 在给定医生的链接约会中搜索最新约会。

【讨论】:

KVO 支持怎么样?我宁愿像普通的瞬态属性一样利用 CoreData 框架。 这是真的,但在大多数情况下,您需要在创建时将实际预约与医生联系起来,反之亦然,因此您仍然需要医生进行实例化。鉴于这是最近的约会。虽然我同意 KVO 是一个很好的方法,但在这种情况下可能有点矫枉过正。 Ofc 这是一个例子。我的应用程序有点复杂。所以KVO就派上用场了。由于关系未保存(瞬态),您如何在应用程序启动时重新建立链接? 老实说,我从来没有需要短暂的关系,所以把所有东西都放在店里,但要知道它们是如何工作的。在这种情况下,虽然我会在商店中保留最新的约会并使用 NSPredicates 来搜索最新的 TBH。主要原因是这两个部分无论如何都在数据存储中,我需要拉出医生或预约才能在此示例中使用。 就像我说的,这只是一个例子。我正在寻找有关如何实现瞬态关系的详细信息。你有什么方便的代码吗?【参考方案3】:

在这种情况下,要覆盖的适当方法是Doctor 实体中的-awakeFromFetch,例如:

- (void)awakeFromFetch 
    [super awakeFromFetch];// important: call this first!
    self.mostRecentAppointment = <something>; // normal  relationship
    self.mostRecentAppointment.doctor = self; // inverse relationship

在模型设计器中,将正常和反向关系都标记为瞬态。应该是这样的。

【讨论】:

你不必同时设置关系和逆关系。设置一个关系,Core Data 会处理反向关系。【参考方案4】:

好吧,您只需在自己的示例程序中尝试一下,正确设置的时间不会超过一个小时。

我的猜测是 --- 不需要额外的编码。如果 Apple 关于 CoreData 的文档是正确的,那么普通属性/关系和“瞬态”之间的唯一区别是后者不会持久化,这意味着当您“保存”时它不会更新持久性存储。

我猜想否则它的所有方面都是完整的,包括 KVO/KVC 合规性、撤消支持、验证和通过删除规则自动更新。唯一的问题是,在重新获取实体后 --- 瞬态关系将始终为零。

为此 --- 我当然不建议将临时关系设置为“非可选”,因为对于大多数实体而言,大多数时间它很可能为空。

我会建立一个反向关系(也是暂时的,并且明智地命名)并且让两个删除规则都是“Nullify”。

到目前为止是暂时的关系。

但是我想出了一个替代方案,试图解决几乎相同的问题。我的“约会”是相关的约会之一,但不仅仅是“最新的”,而是第一个“未完成”的约会。非常相似的逻辑。

我添加了一个新的计算属性到我的“医生”实体生成的 NSManagedObject 子类中,而不是暂时的关系,如下所示:

@interface XXDoctor (XXExtensions)
/**
 @brief Needs manual KVO triggering as it is dependent on a collection. 
        Alternatively, you can observe insertions and deletions of the appointments, and trigger KVO on this propertyOtherwise it can be auto-
 @return the latest of the to-many appointments relation.
 **/
@property (readonly) XXAppointment *latestAppointment;    // defined as the
@end

实施:

#import "XXDoctor".h"
#import "XXAppointment.h"
@implementation XXDoctor (XXExtensions)
// this won't work because "appointments" is a to-many relation.
//+ (NSSet *)keyPathsForValuesAffectingLatestAppointment 
//  return [NSSet setWithObjects:@"appointments", nil];
//
- (XXAppointment *) latestAppointment 
    NSInteger latestAppointmentIndex = [self.appointments indexOfObjectPassingTest:^BOOL(XXAppointment *appointment, NSUInteger idx, BOOL *stop) 
        *stop =  (appointment.dateFinished == nil);
        return *stop;
    ];
    return (latestAppointmentIndex == NSNotFound) ? nil : [self.appointments objectAtIndex: latestAppointmentIndex];

@end

【讨论】:

抱歉,您还没有真正回答我的问题,即如何正确实现瞬态关系。我无法在 Apple 文档中找到有关此主题的更多信息。你有这个特定主题的链接吗? 我做到了。不需要实施。只需将关系标记为“瞬态”,重新生成您的 NSManagedObject 子类,然后随意使用。没有进一步实施。我现在在我的应用程序中成功地使用了一对一和一对多的瞬态关系,没有任何特殊问题。 啊,在我的应用程序中,我还定义了与瞬态关系的反比关系,并将它们也设置为瞬态。我在我的应用程序中使用瞬态关系(而不是我建议的计算属性)的原因是 UndoManager 将它们视为正常的 CoreData 更改,并且知道如何优雅地还原它们。

以上是关于CoreData 瞬态关系示例的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CoreData 中正确建立复杂的关系?

CoreData 在一对多关系中编辑一个属性

如何基于另一个实体的一对多关系访问 CoreData 实体?

xcode iOS CoreData 简单关系数据检索

核心数据,“按瞬态属性排序”解决方法

将属性更改为不再是瞬态的 - 我可以通过轻量级核心数据迁移来做到这一点吗?