核心数据独特属性
Posted
技术标签:
【中文标题】核心数据独特属性【英文标题】:Core Data unique attributes 【发布时间】:2010-02-10 19:42:48 【问题描述】:是否可以使 Core Data 属性唯一,即没有两个 MyEntity 对象可以具有相同的 myAttribute?
我知道如何以编程方式执行此操作,但我希望有一种方法可以使用 xcode 中的图形数据模型编辑器来执行此操作。
我正在使用 iPhone 3.1.2 SDK。
【问题讨论】:
为什么这个问题被关闭了?给出的原因是“要求代码的问题必须证明对正在解决的问题的最小理解”,但我根本没有要求代码 - 我想知道是否可以在 Xcode 的图形编辑器中实现这一点。 (答案实际上是“不,但这里有一些解决方法”。) 【参考方案1】:每次在对象上创建时,我都会执行一个类方法,该方法仅在另一个实体不存在时创建一个新实体。
+ (TZUser *)userWithUniqueUserId:(NSString *)uniqueUserId inManagedObjectContext:(NSManagedObjectContext *)context
TZUser *user = nil;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"TZUser" inManagedObjectContext:context];
request.predicate = [NSPredicate predicateWithFormat:@"objectId = %@", uniqueUserId];
NSError *executeFetchError = nil;
user = [[context executeFetchRequest:request error:&executeFetchError] lastObject];
if (executeFetchError)
NSLog(@"[%@, %@] error looking up user with id: %i with error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [uniqueUserId intValue], [executeFetchError localizedDescription]);
else if (!user)
user = [NSEntityDescription insertNewObjectForEntityForName:@"TZUser"
inManagedObjectContext:context];
return user;
【讨论】:
这是我见过的对这种设计模式最简单、最清晰的理解。谢谢! 这是一个非常棒的解决方案。看到之后,感觉很明显。现在我仍然是一个新手,所以也许这有一些缺点,但据我所知,它对我的应用程序非常有用。感谢分享! 这种方法的问题是你会产生 IO。如何避免? 您基本上是在每次 INSERT 之前执行 SELECT。这不是被认为太“贵”了吗? 在else if
子句中创建新用户对象后,您可能还希望将 objectID
设置为 uniqueUserID
。否则,您必须在调用 userWithUniqueUserID
的函数中执行此操作。【参考方案2】:
从 ios 9 开始,有一种处理唯一约束的新方法。
您在数据模型中定义唯一属性。
您需要设置一个托管上下文合并策略“合并策略单例对象,该策略定义了在保存操作期间处理冲突的标准方法”NSErrorMergePolicy 是默认设置,如果存在任何合并冲突,此策略会导致保存失败。
- (NSManagedObjectContext *)managedObjectContext
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator)
return nil;
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
[_managedObjectContext setMergePolicy:NSOverwriteMergePolicy];
return _managedObjectContext;
Apple Ducumentation Merge Policy 讨论了各种选项
这里回答的很好 Zachary Orr's Answer
他还创建了一篇博文和示例代码。
Sample Code
Blog Post
最具挑战性的部分是让数据模型属性可编辑。秘诀是左键单击,然后右键单击,然后单击+号添加约束。
【讨论】:
Xcode 版本 11.4 (11E146):上述答案中最重要的陈述是。 “秘诀是左键单击,然后右键单击,然后单击 + 号添加约束。”谢谢@Ryan Heitner ?【参考方案3】:我决定使用validate<key>:error:
方法来检查是否已经存在特定值为<key>
的托管对象。如果是这种情况,则会引发错误。
例如:
- (BOOL)validateMyAttribute:(id *)value error:(NSError **)error // 如果已经存在具有 myAtribute 值的对象,则返回 NO
感谢 Martin Cote 的意见。
【讨论】:
查询时,请确保使用self.managedObjectContext
,以确保在正确的managedObjectContext
中检查是否存在重复项。否则,另存为(即迁移到新的 url)会失败。
执行 fetch 将更改托管对象上下文中的对象图 - 这不是您想要在验证方法中执行的操作!
不一定 - 如果您只查询但不插入/更新/删除任何实体,则上下文不会因验证而变脏。不建议在验证中引入编辑(弄脏您的上下文),因为它会导致递归 - 在某些情况下仍然可以。
您可以通过检查是否以前保存和字段未更改来进行优化。【参考方案4】:
您可以覆盖setMyAttribute
方法(使用类别)并确保在那里的唯一性,尽管这可能很昂贵:
- (void)setMyAttribute:(id)value
NSArray *objects = [self fetchObjectsWithMyValueEqualTo:value];
if( [objects count] > 0 ) // ... throw some exception
[self setValue:value forKey:@"myAttribute"];
如果您想确保每个MyEntity
实例都有一个不同的myAttribute
值,您可以使用NSManagedObject
对象的objectID
。
【讨论】:
感谢您的快速回复! “您可以覆盖 setMyAttribute” 这就是我目前正在做的事情。值得庆幸的是,数据集足够小,获得所有对象的列表并不太昂贵,尽管我希望有一个更优雅的解决方案是可能的。 “如果你想确保每个 MyEntity 实例都有一个不同的 myAttribute 值......”这正是我想要做的,尽管我正在努力了解如何将对象的 ID 用于此目的。是否可以以某种方式为每个对象分配您自己的 ID? @robinjam,我更多的是在谈论这样一个事实,即您可以将 objectID 值插入“myAttribute”中的某处以确保每个值都是唯一的。 哦,我明白你的意思了。但是,有问题的属性实际上是用户提供的名称,因此这不适合我的目的。我想让它们保持唯一的原因是用户无法区分两个具有相同名称的对象。请参阅我最终确定的解决方案的答案。感谢您的意见! @MartinCote 什么是 fetchObjectsWithMyValueEqualTo ?这又是像 doozMen 一样的 SELECT 吗?【参考方案5】:我真的很喜欢@DoozMen 的方法!! 我认为这是做我需要做的最简单的方法。
这是我将它安装到我的项目中的方式:
以下代码循环绘制一个相当长的 tableView,为每个表行保存一个对象到 DB,并为每个对象设置各种对象属性,如 UISwitch
状态和其他内容:如果行的对象带有某些标签不存在于数据库中,它会创建它。
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@"Obiettivo" inManagedObjectContext:self.managedObjectContext];
request.predicate = [NSPredicate predicateWithFormat:@"obiettivoID = %d", obTag];
NSError *executeFetchError = nil;
results = [[self.managedObjectContext executeFetchRequest:request error:&executeFetchError] lastObject];
if (executeFetchError)
NSLog(@"[%@, %@] error looking up for tag: %i with error: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), obTag, [executeFetchError localizedDescription]);
else if (!results)
if (obbCD == nil)
NSEntityDescription *ent = [NSEntityDescription entityForName:@"Obiettivo" inManagedObjectContext:self.managedObjectContext];
obbCD = [[Obiettivo alloc] initWithEntity:ent insertIntoManagedObjectContext:self.managedObjectContext];
//set the property that has to be unique..
obbCD.obiettivoID = [NSNumber numberWithUnsignedInt:obTag];
[self.managedObjectContext insertObject:obbCD];
NSError *saveError = nil;
[self.managedObjectContext save:&saveError];
NSLog(@"added with ID: %@", obbCD.obiettivoID);
obbCD = nil;
results = nil;
【讨论】:
【参考方案6】:查看 Apple documentation 进行属性间验证。它描述了如何在查询整个数据库的同时验证特定的插入或更新操作。
【讨论】:
这似乎只是关于验证值。不检查现有记录。【参考方案7】:你只需要检查一个现有的:/
我只是看不到核心数据真正提供的任何帮助。约束功能以及被破坏的功能并没有真正发挥作用。当然,在所有现实世界的情况下,您只需要检查一个是否已经存在,如果存在则使用那个(例如,作为另一个项目的关系字段,当然)。我只是看不到任何其他方法。
为了节省任何人的输入...
// you've download 100 new guys from the endpoint, and unwrapped the json
for guy in guys
// guy.id uniquely identifies
let g = guy.id
let r = NSFetchRequest<NSFetchRequestResult>(entityName: "CD_Guy")
r.predicate = NSPredicate(format: "id == %d", g)
var found: [CD_Guy] = []
do
let f = try core.container.viewContext.fetch(r) as! [CD_Guy]
if f.count > 0 continue // that's it. it exists already
catch
print("basic db error. example, you had = instead of == in the pred above")
continue
CD_Guy.make(from: guy) // just populate the CD_Guy
save here: core.saveContext()
or save here: core.saveContext()
core
只是你的单身人士,不管你的上下文和其他东西。
请注意,在示例中,您可以在每次添加新内容时保存上下文,或者之后一次全部保存。
(我发现表格/集合绘制速度如此之快,与 CD 结合使用,这真的无关紧要。)
(别忘了 .privateQueueConcurrencyType )
请注意,此示例不显示基本上,您创建实体并在另一个上下文中写入,并且您必须使用 .privateQueueConcurrencyType
您不能使用与表相同的上下文/collections .. .viewContext .
let pmoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
pmoc.parent = core.container.viewContext
do try pmoc.save() catch fatalError("doh \(error)")
【讨论】:
以上是关于核心数据独特属性的主要内容,如果未能解决你的问题,请参考以下文章