草庐IT

关于objective C:Core Data 独特属性

codeneng 2023-03-28 原文

Core Data unique attributes

是否可以使 Core Data 属性唯一,即没有两个 MyEntity 对象可以具有相同的 myAttribute?

我知道如何以编程方式执行此操作,但我希望有一种方法可以使用 xcode 中的图形数据模型编辑器来执行此操作。

我正在使用 iPhone 3.1.2 SDK。

  • 为什么这个问题被关闭了?给出的原因是"要求代码的问题必须证明对正在解决的问题有最低限度的理解",但我根本没有要求代码 - 我想知道是否可以在图形编辑器中实现这一点在 Xcode 中。 (答案实际上是"不,但这里有一些解决方法"。)


每次在对象上创建时,我都会执行一个类方法,该方法仅在另一个实体不存在时创建一个新实体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ (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。如何避免这种情况?
  • 招致 IO 是什么意思。我想解决这个问题,但不确定问题是什么。我通常在单独的私有队列中执行此操作,因此它不会阻塞主线程。
  • 您基本上是在每次 INSERT 之前执行 SELECT。这不是被认为太"贵"吗?
  • 同意并对此表示赞同,但这是我从苹果文档中得到的。估计挺贵的……
  • else if 子句中创建新用户对象后,您可能还希望将 objectID 设置为 uniqueUserID。否则,您必须在调用 userWithUniqueUserID 的函数中执行此操作。
  • 如果您有超过 1 个线程尝试将数据保存在具有公共父上下文的单独上下文中,它将不起作用。它有时可能会起作用,但它在这里绝对是竞争条件。
  • 这是 find-or-create 模式的一个示例,它是使用 Core Data 完成此任务的首选方法。 @NikitaTook,如果正确完成,它确实可以很好地处理父子上下文。将其推送给父级并在父级中对其进行故障处理。
  • @quellish 你可以发布一个链接或关于这个的东西。我对此一无所知,我通常会在同一线程上的 dispatch_async 中调用任何保存上下文以获得线程安全。至于答案,是否包含处理"已找到唯一 ID"的逻辑不是更明显,但我假设您在其他地方这样做,因为您也没有在此方法中设置 objectID
  • 这不只是以编程方式进行吗?我认为这个问题是不要以编程方式进行。
  • 这很棒!我们可以有快速版本吗?


从 IOS 9 开始,有一种处理唯一约束的新方法。

您在数据模型中定义独特的属性。

您需要设置托管上下文合并策略"合并策略单例对象,该策略定义了在保存操作期间处理冲突的标准方法"NSErrorMergePolicy 是默认设置,如果存在任何合并冲突,此策略会导致保存失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (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 中讨论了各种选项

这里得到了很好的回答
扎卡里·奥尔的回答

他还创建了一篇博文和示例代码。

示例代码

博文

最具挑战性的部分是让数据模型属性可编辑。秘诀是左键单击,然后右键单击,然后单击符号添加约束。

  • Xcode 版本 11.4 (11E146):上述答案中最重要的陈述是。 "秘密是左键单击,然后右键单击,在您单击标志添加约束之后。"谢谢@Ryan Heitner ??


我决定使用 validate<key>:error: 方法来检查是否已经存在具有特定值 <key> 的托管对象。如果是这种情况,则会引发错误。

例如:

1
2
3
- (BOOL)validateMyAttribute:(id *)value error:(NSError **)error {
    // Return NO if there is already an object with a myAtribute of value
}

感谢 Martin Cote 的意见。

  • 查询时,请确保使用 self.managedObjectContext,以确保在正确的 managedObjectContext 中检查是否存在重复项。否则,另存为(即迁移到新 url)将失败。
  • 执行 fetch 将更改托管对象上下文中的对象图 - 这不是您想要在验证方法中执行的操作!
  • 不一定 - 如果您只查询但不插入/更新/删除任何实体,则上下文不会因验证而变脏。不建议在验证中引入编辑(弄脏您的上下文),因为它会导致递归 - 在某些情况下仍然可以。
  • 您可以通过检查是否以前保存和字段未更改来进行优化。


您可以覆盖 setMyAttribute 方法(使用类别)并确保在那里的唯一性,尽管这可能很昂贵:

1
2
3
4
5
6
- (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 吗?


您只需要检查现有的:/

我只是看不到核心数据真正提供的任何帮助。约束功能以及被破坏的功能并没有真正发挥作用。当然,在所有现实世界的情况下,您只需要检查一个是否已经存在,如果存在则使用那个(例如,作为另一个项目的关系字段,当然)。我只是看不到任何其他方法。

为了节省任何人的输入...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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 您不能使用与表/集合相同的上下文.. .viewContext .

1
2
3
let pmoc = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
pmoc.parent = core.container.viewContext
do { try pmoc.save() } catch { fatalError("doh \\(error)")}

查看 Apple 文档以进行属性间验证。它描述了如何在查询整个数据库的同时验证特定的插入或更新操作。

  • 这似乎只是关于验证值。不检查现有记录。


我真的很喜欢@DoozMen 的方法!!
我认为这是做我需要做的最简单的方法。

这是我将它安装到我的项目中的方式:

以下代码循环绘制一个相当长的tableView,为每个表行保存一个对象到DB,并为每个对象设置各种对象属性,如UISwitch状态和其他事情:如果行的对象带有a某些标签不存在于数据库中,它会创建它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;

有关关于objective C:Core Data 独特属性的更多相关文章

  1. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  2. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  3. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  4. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  5. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  6. ruby - Chef Ruby 遍历 .erb 模板文件中的属性 - 2

    所以这可能有点令人困惑,但请耐心等待。简而言之,我想遍历具有特定键值的所有属性,然后如果值不为空,则将它们插入到模板中。这是我的代码:属性:#===DefaultfileConfigurations#default['elasticsearch']['default']['ES_USER']=''default['elasticsearch']['default']['ES_GROUP']=''default['elasticsearch']['default']['ES_HEAP_SIZE']=''default['elasticsearch']['default']['MAX_OP

  7. ruby - 获取数组中的值并最小化某个类属性的最优雅的方法是什么? - 2

    假设我有以下类(class):classPersondefinitialize(name,age)@name=name@age=ageenddefget_agereturn@ageendend我有一组Person对象。是否有一种简洁的、类似于Ruby的方法来获取最小(或最大)年龄的人?如何根据它对它们进行排序? 最佳答案 这样做会:people_array.min_by(&:get_age)people_array.max_by(&:get_age)people_array.sort_by(&:get_age)

  8. ruby-on-rails - 为模型创建状态属性 - 2

    我想为我的Task模型创建一个status属性,该属性将按以下顺序指示它在三部分进度中的位置:打开=>进行中=>完成。它的工作方式类似于亚马逊包裹的交付方式:已订购=>已发货=>已交付。我想知道设置此属性的最佳方法是什么。我可能是错的,但创建三个独立的bool属性似乎有点多余。实现此目标的最佳方法是什么? 最佳答案 Rails4有一个内置的enummacro.它使用单个整数列并映射到键列表。classOrderenumstatus:[:ordered,:shipped,:delivered]end状态映射如下:{ordered:0,

  9. ruby - Chef LW 资源属性默认值如何引用另一个属性? - 2

    我正在尝试将一个资源属性的默认值设置为另一个属性的值。我正在为我正在构建的tomcat说明书定义一个资源,其中包含以下定义。我想要可以独立设置的“名称”和“服务名称”属性。当未设置服务名称时,我希望它默认为为“名称”提供的任何内容。以下不符合我的预期:attribute:name,:kind_of=>String,:required=>true,:name_attribute=>trueattribute:service_name,:kind_of=>String,:default=>:name注意第二行末尾的“:default=>:name”。当我在Recipe的新block中引用我

  10. ruby-on-rails - 关于 Ruby 的一般问题 - 2

    我在我的rails应用程序中安装了来自github.com的acts_as_versioned插件,但有一段代码我不完全理解,我希望有人能帮我解决这个问题class_eval我知道block内的方法(或任何它是什么)被定义为类内的实例方法,但我在插件的任何地方都找不到定义为常量的CLASS_METHODS,而且我也不确定是什么here,并且有问题的代码从lib/acts_as_versioned.rb的第199行开始。如果有人愿意告诉我这里的内幕,我将不胜感激。谢谢-C 最佳答案 这是一个异端。http://en.wikipedia

随机推荐