这是加载现有否则创建新 NSManagedObjects 的最佳方式吗?

Posted

技术标签:

【中文标题】这是加载现有否则创建新 NSManagedObjects 的最佳方式吗?【英文标题】:Is this the best way to load existing otherwise create new NSManagedObjects? 【发布时间】:2015-11-19 08:17:29 【问题描述】:

我需要通过 API 加载大量数据,并使用它来创建新对象或更新现有对象及其关系。这是正确的方法吗?好像很啰嗦,我觉得我在这里漏掉了一些东西。

// Check for existing Object
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"name == %@", objectName]];
[fetchRequest setFetchLimit:1];

NSError *error = nil;
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (results == nil) 
    NSAssert(NO, @"Error executing fetch: %@\n%@", [error localizedDescription], [error userInfo]);


Object *object = [results lastObject];

if (object == nil) 
    // Create new object
    object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:managedObjectContext];
    object.name = objectName;



// Check for existing Other
fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Other" inManagedObjectContext:managedObjectContext]];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"name == %@", otherName]];
[fetchRequest setFetchLimit:1];

results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (results == nil) 
    NSAssert(NO, @"Error executing fetch: %@\n%@", [error localizedDescription], [error userInfo]);


Other *other = [results lastObject];

if (other == nil) 
    // Create new Other
    other = [NSEntityDescription insertNewObjectForEntityForName:@"Other" inManagedObjectContext:managedObjectContext];
    other.name = otherName;



// Finally
[object addOtherObject:other]; // A many-to-many relationship

if (![managedObjectContext save:&error]) 
    NSAssert(NO, @"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);

所有这些当然都在一个循环中,感觉非常低效。

【问题讨论】:

看看 Magical Record 和 Mogenerator。 Core Data 充满了样板代码,这些库删除了大部分代码 一切都是关于集合的。与其在循环中一次获取一个实体,不如一次获取所有实体。然后您可以找出结果中不存在哪些实体并创建这些实体。 【参考方案1】:

有很多方法可以最大限度地减少样板。我创建了一个NSManagedObject 子类,用作所有模型对象的基础。它包含如下方法:

+ (NSString *)entityName;

+ (instancetype)newEntityInContext:(ManagedObjectContext *)context;
+ (instancetype)newEntityInContext:(ManagedObjectContext *)context properties:(NSDictionary *)properties;

+ (NSArray *)entitiesWithPredicate:(NSPredicate *)predicate inContext:(RoundsManagedObjectContext *)context;
+ (NSArray *)entitiesWithPredicate:(NSPredicate *)predicate
                      sortedBy:(NSArray *)sortDescriptors
                     inContext:(ManagedObjectContext *)context;

+ (NSArray *)entitiesWithProperties:(NSDictionary *)properties inContext:(ManagedObjectContext *)context;

+ (NSInteger)countOfEntitiesWithPredicate:(NSPredicate *)predicate inContext:(ManagedObjectContext *)context;

我让你看看这些如何处理创建获取请求等的样板文件。它确实使您实际上使用核心数据的代码更具可读性。

现在,说说循环获取的效率:

不要。也就是说,不要进行单独的提取。相反,将objectNames 收集到一个数组中并一次获取所有相关对象。假设你实现的方法类似于我上面写的,你可以有如下所示的伪代码:

NSArray *objectNames = ...;
NSArray *otherNames = ...;

NSManagedObjectContext *context = ...;

NSArray *existingObjects = [Object entitiesWithPredicate:[NSPredicate predicateWithFormat:@"name IN %@", objectNames];
NSArray *existingOthers = [Other entitiesWithPredicate:[NSPredicate predicateWithFormat:@"name IN %@", otherNames];

NSArray *missingObjectNames = [objectNames removeObjects:[existingObjects valueForKeyPath:@"name"]];

for (NSString *name in missingObjectNames) 
    Object *object = [Object newEntityInContext:context];
    object.name = name;

    // Do stuff with Other objects here.

【讨论】:

注意:我没有提供Other 的代码,因为您不太清楚您如何确定哪个otherName 与哪个Object 搭配。希望我已经提供了足够的基础来弄清楚您需要添加什么。

以上是关于这是加载现有否则创建新 NSManagedObjects 的最佳方式吗?的主要内容,如果未能解决你的问题,请参考以下文章

将 HTML 表格数据导出到现有工作簿中的新工作表(如果存在),否则更新现有工作表

通过VBA如果打开则使用现有数据库,否则打开新数据库然后关闭

为现有实体创建新的 CoreData 属性时 Swift 崩溃

CoreData 迁移和数据映射:从现有属性创建新实体

PHP基于父类的现有实例创建子类的新实例

使用外键发布新实体会导致创建另一个外部实体而不是引用现有实体