iOS Core Data:为 Rails 多态关联设置关系的方法
Posted
技术标签:
【中文标题】iOS Core Data:为 Rails 多态关联设置关系的方法【英文标题】:iOS Core Data: Approach for setting relationship for Rails polymorphic association 【发布时间】:2011-04-19 15:26:03 【问题描述】:我有一个带有 Core Data 模型的 ios 应用程序,该模型模仿了我的 Rails 后端数据模型。在我的 Rails 后端模型中,我对一些实体使用了多态关联。我的 Rails 模型如下所示:
Airport < ActiveRecord::Base
has_many :reviews, :as => :reviewable
Restaurant < ActiveRecord::Base
has_many :reviews, :as => :reviewable
Review < ActiveRecord::Base
belongs_to :reviewable, :polymorphic => :true
在我的核心数据模型中,我有三个独立的实体,MyAirport、MyRestaurant 和 MyReview,相关属性如下所示:
MyAirport
@property (nonatomic, retain) NSSet* reviews; //inverse is airport
MyRestaurant
@property (nonatomic, retain) NSSet* reviews; //inverse is restaurant
MyReview
@property (nonatomic, retain) NSNumber* reviewablId;
@property (nonatomic, retain) NSString* reviewableType;
@property (nonatomic, retain) MyAirport* airport; //inverse is reviews
@property (nonatomic, retain) MyRestaurant* restaurant; //inverse is reviews
我的问题与 Core Data 中的 MyReview 类有关。根据 reviewableId 和 reviewableType 中的值设置正确的核心数据关系(例如机场或餐厅)的最佳方式是什么?
我尝试了以下方法,但由于某种原因,所有这些方法似乎都有些不干净。一般来说,在设置正确的关联之前,我需要确保我有可用的可审查 ID 和可审查类型,这似乎是与感知清洁度相关的主要问题。当从 JSON 水合这些模型对象时,我并没有真正控制在新的 MyReview 对象上填充 reviewableId 和 reviewableType 的顺序。
1) 在一个或两个 reviewableId 和 reviewableType 上的自定义设置器 - 我最终在每个设置器方法中使用逻辑来检查另一个属性的值,以确保在我可以在 MyReview 模型上设置机场或餐厅关系之前填充它目的。一般来说,这是可行的,但感觉不太对劲,而且每个 setter 中都有一些难看的重复代码。
- (void)setReviewableId:(NSNumber*)reviewableId
[self willChangeValueForKey:@"reviewableId"];
[self setPrimitiveReviewableId:reviewableId];
[self didChangeValueForKey:@"reviewableId"];
if (reviewableId && [reviewableId intValue] != 0 && self.reviewableType)
if (self.airport == nil && [self.reviewableType isEqualToString:@"Airport"])
MyAirport myAirport = <... lookup MyAirport by airportId = reviewableId ...>
if (myAirport)
self.airport = myAirport;
else if (self.restaurant == nil && [self.reviewableType isEqualToString:@"Restaurant"])
MyRestaurant myRestaurant = <... lookup MyRestaurant by restaurantId = reviewableId ...>
if (myRestaurant)
self.restaurant = myRestaurant;
- (void)setReviewableType:(NSString*)reviewableType
[self willChangeValueForKey:@"reviewableType"];
[self setPrimitiveReviewableType:reviewableType];
[self didChangeValueForKey:@"reviewableType"];
if (self.reviewableId && [self.reviewableId intValue] != 0 && reviewableType)
if (self.airport == nil && [reviewableType isEqualToString:@"Airport"])
MyAirport myAirport = <... lookup MyAirport by airportId = self.reviewableId ...>
if (myAirport)
self.airport = myAirport;
else if (self.restaurant == nil && [reviewableType isEqualToString:@"Restaurant"])
MyRestaurant myRestaurant = <... lookup MyRestaurant by restaurantId = self.reviewableId ...>
if (myRestaurant)
self.restaurant = myRestaurant;
2) reviewableId 和 reviewableType 上的 KVO - 在 awakeFromInsert 和 awakeFromFetch 中,我通过 KVO 将模型对象注册到观察者自己的 reviewableId 和 reviewableType 属性。让对象观察自己感觉真的很难看,但至少我有一个处理填充关联的方法,这似乎比 #1 有所改进。请注意,我确实在 dealloc 方法中使用 removeObserver:forKeyPath: 导致了一些崩溃(例如,崩溃表明我已经删除了一个不存在的观察者??),所以这远不是经过验证的代码。
- (void)awakeFromFetch
[super awakeFromFetch];
[self addObserver:self forKeyPath:@"reviewableId" options:0 context:nil];
[self addObserver:self forKeyPath:@"reviewableType" options:0 context:nil];
- (void)awakeFromInsert
[super awakeFromInsert];
[self addObserver:self forKeyPath:@"reviewableId" options:0 context:nil];
[self addObserver:self forKeyPath:@"reviewableType" options:0 context:nil];
- (void)dealloc
[self removeObserver:self forKeyPath:@"reviewableId"];
[self removeObserver:self forKeyPath:@"reviewableType"];
[super dealloc];
- (void)updatePolymorphicAssociations
if (self.reviewableId && [self.reviewableId intValue] != 0 && self.reviewableType)
if (self.airport == nil && [self.reviewableType isEqualToString:@"Airport"])
MyAirport myAirport = <... lookup MyAirport by airportId = self.reviewableId ...>
if (myAirport)
self.airport = myAirport;
else if (self.restaurant == nil && [self.reviewableType isEqualToString:@"Restaurant"])
MyRestaurant myRestaurant = <... lookup MyRestaurant by restaurantId = self.reviewableId ...>
if (myRestaurant)
self.restaurant = myRestaurant;
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
[self updatePolymorphicAssociations];
3) 使用上面的 updatePolymorphicAssociations 方法覆盖 willSave。这似乎可行,但它将所有对关联的更改推迟到我们去保存对象之前,这通常不是在我们对 reviewableId 和 reviewableType 进行初始更改时。这里似乎也有一些限制,与通过后台线程插入一堆新的 MyReview 对象有关,可能与 willSave 实际上导致我们刚刚保存的对象发生更改有关,从而再次触发 willSave。即使在 updatePolymorphicAssociations 中检查了 nil,我似乎仍然能够使用这种方法使应用程序崩溃。
- (void)updatePolymorphicAssociations
if (self.reviewableId && [self.reviewableId intValue] != 0 && self.reviewableType)
if (self.airport == nil && [self.reviewableType isEqualToString:@"Airport"])
MyAirport myAirport = <... lookup MyAirport by airportId = self.reviewableId ...>
if (myAirport)
self.airport = myAirport;
else if (self.restaurant == nil && [self.reviewableType isEqualToString:@"Restaurant"])
MyRestaurant myRestaurant = <... lookup MyRestaurant by restaurantId = self.reviewableId ...>
if (myRestaurant)
self.restaurant = myRestaurant;
- (void)willSave
[self updatePolymorphicAssociations];
4) 覆盖 didSave,而不是 willSave。这解决了我们在 #3 中获得的在后台大量导入的保存问题,但最终我们得到了未保存的更改,这很丑陋。
所以,我在这里似乎有很多选择,但没有一个适合使用由 Rails 云数据库支持的 Core Data 来解决 iOS 应用程序中看似常见的问题。我是否过度分析了这一点?还是有更好的办法?
更新:请注意,目标是仅设置 MyReview 上的一种关系,基于可审查类型是“机场”还是“餐厅”。这种关系设置本身就很丑陋,但这就是我问这个问题的原因。 :)
【问题讨论】:
澄清:如果 reviewableID 和 reviewableType 有任何值,您是否只想设置任何一个关系,还是必须有特定值? 【参考方案1】:您似乎犯了一个重大错误,将 Core Data 视为关系数据库的对象包装器。它不是。 ActiveRecord 是一个围绕关系数据库的对象包装器,这就是它必须使用多态关联等技术的原因。你不能将 Core Data 塞进关系包装范式中并期望得到一个优雅的设计。
根据我对您的数据模型的困惑,您实际上并不需要 reviewablId
和
reviewableType
属性来实际创建图形本身。相反,您将用实体继承和关系替换它们。从看起来像这样的数据模型开始。
Review
//... some review attributes
place<<-(required)->ReviewablePlace.reviews //... logically a review must always have a place that is reviewed
ReviewablePlace(abstract)
reviewableID:Number //... in case you have to fetch on this ID number later
reviews<-->>Review.place
Airport:Reviewable
//... some airport specific attributes
Restaurant:Reviewable
//... some restaurant specific attributes
这用对象关系替换了多态关联,并通过制作两个单独的子实体来区分两种类型的可审查地点。由于Review
实体具有使用抽象Reviewable
实体定义的关系,它还可以与任何Reviewable
子实体形成关系。您可以通过Reviewable
实体上的reviewableID 获取,它将返回具有该ID 的Airport
或Restaurant
对象,您可以弹出新Review
对象的place
关系。
要处理你的 JSON 对象,你会(在伪代码中):
if Airport
fetch on reviewableID
if fetch returns existing Airport managed object
break //don't want a duplicate so don't create an object
else
create new Airport managed object with reviewableID
break
if Restaurant
fetch on reviewableID
if fetch returns existing Restaurant managed object
break //don't want a duplicate so don't create an object
else
create new Restaurant managed object with reviewableID
break
if Review
fetch on reviewableID
if fetch returns Reviewable object of either subclass
create new Review object and assign fetched Reviewable object to the Review.place relationship
break
else
create new Airport or Resturant object using the JSON provided reviewableID and reviewableType info
create new Review object and assign the newly created Reviewable subclass object to the Review.place relationship
break
我认为这将为您提供一个完整的对象图,而无需跳过箍。在任何情况下,您在父帖子中考虑的大部分检查都可以替换为子实体和适当的关系。
【讨论】:
肯定喜欢这里使用继承来解决问题。感谢您的回答! 投反对票,因为这似乎是对继承的令人发指的滥用。以上是关于iOS Core Data:为 Rails 多态关联设置关系的方法的主要内容,如果未能解决你的问题,请参考以下文章