核心数据查找或创建最有效的方法 [英] Core data find-or-create most efficient way

查看:146
本文介绍了核心数据查找或创建最有效的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有大约10000个对象的实体'消息'。当我添加一个新的消息'我想先看看它是否存在 - 如果它只是更新它的数据,但如果它不创建它。

I have around 10000 objects of entity 'Message'. When I add a new 'Message' i want to first see whether it exists - and if it does just update it's data, but if it doesn't to create it.

现在,查找或创建算法通过保存所有的Message对象 objectID '在一个数组中,然后通过它们进行过滤,并获取 existingObjectWithID:错误:

Right now the "find-or-create" algorithm works with by saving all of the Message objects 'objectID' in one array and then filtering through them and getting the messages with existingObjectWithID:error:

在我的情况下,当我使用 existingObjectWithID :获取一个消息,然后尝试设置和保存属性通过设置消息对象的属性,并调用save:on它的上下文它不能正确保存。有没有人遇到类似这样的问题?

This works fine but in my case when I fetch an 'Message' using existingObjectWithID: and then try to set and save a property by setting the property of the 'Message' object and calling save: on it's context it doesn't saves it properly. Has anyone come across a problem like this?

有更有效的方法来进行查找或创建算法吗?

Is there a more efficient way to make find-or-create algorithm?

推荐答案

首先,消息是CoreData实体的坏名称,因为apple在内部使用它,

您可以阅读更多关于它此处

First, Message is a "bad" name for a CoreData entity as apple use it internally and it cause problems later in development.
You can read a little more about it HERE

我注意到所有建议的解决方案都使用数组或获取请求。

您可能需要考虑一个基于字典的解决方案...

I've noticed that all suggested solutions here use an array or a fetch request.
You might want to consider a dictionary based solution ...

在单线程/上下文应用程序中,通过向缓存(字典)添加新插入的对象(类型消息)并使用现有对象标识和键映射预填充缓存,从而导致过多的负担。 />

In a single threaded/context application this is accomplished without too much of a burden by adding to cache (dictionary) the newly inserted objects (of type Message) and pre-populating the cache with existing object ids and keys mapping.

请考虑使用此界面:

Consider this interface:

@interface UniquenessEnforcer : NSObject

@property (readonly,nonatomic,strong) NSPersistentStoreCoordinator* coordinator;
@property (readonly,nonatomic,strong) NSEntityDescription* entity;
@property (readonly,nonatomic,strong) NSString* keyProperty;
@property (nonatomic,readonly,strong) NSError* error;

- (instancetype) initWithEntity:(NSEntityDescription *)entity
                    keyProperty:(NSString*)keyProperty
                    coordinator:(NSPersistentStoreCoordinator*)coordinator;

- (NSArray*) existingObjectIDsForKeys:(NSArray*)keys;
- (void) unregisterKeys:(NSArray*)keys;
- (void) registerObjects:(NSArray*)objects;//objects must have permanent objectIDs
- (NSArray*) findOrCreate:(NSArray*)keys
                  context:(NSManagedObjectContext*)context
                    error:(NSError* __autoreleasing*)error;
@end

流程:

1)在应用程序启动时,分配唯一性强制器并填充缓存:

1) on application start, allocate a "uniqueness enforcer" and populate your cache:

//private method of uniqueness enforcer
- (void) populateCache
{
    NSManagedObjectContext* context = [[NSManagedObjectContext alloc] init];
    context.persistentStoreCoordinator = self.coordinator;

    NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:self.entity.name];
    [r setResultType:NSDictionaryResultType];

    NSExpressionDescription* objectIdDesc = [NSExpressionDescription new];
    objectIdDesc.name = @"objectID";
    objectIdDesc.expression = [NSExpression expressionForEvaluatedObject];
    objectIdDesc.expressionResultType = NSObjectIDAttributeType;

    r.propertiesToFetch = @[self.keyProperty,objectIdDesc];

    NSError* error = nil;

    NSArray* results = [context executeFetchRequest:r error:&error];
    self.error = error;
    if (results) {
        for (NSDictionary* dict in results) {
            _cache[dict[self.keyProperty]] = dict[@"objectID"];
        }
    } else {
        _cache = nil;
    }
}

2)当你需要测试存在时,

2) when you need to test existence simply use:

- (NSArray*) existingObjectIDsForKeys:(NSArray *)keys
{
    return [_cache objectsForKeys:keys notFoundMarker:[NSNull null]];
}

3)当你想实际获取对象并创建缺少的对象时:

3) when you like to actually get objects and create missing ones:

- (NSArray*) findOrCreate:(NSArray*)keys
                  context:(NSManagedObjectContext*)context
                    error:(NSError* __autoreleasing*)error
{
    NSMutableArray* fullList = [[NSMutableArray alloc] initWithCapacity:[keys count]];
    NSMutableArray* needFetch = [[NSMutableArray alloc] initWithCapacity:[keys count]];

    NSManagedObject* object = nil;
    for (id<NSCopying> key in keys) {
        NSManagedObjectID* oID = _cache[key];
        if (oID) {
            object = [context objectWithID:oID];
            if ([object isFault]) {
                [needFetch addObject:oID];
            }
        } else {
            object = [NSEntityDescription insertNewObjectForEntityForName:self.entity.name
                                                   inManagedObjectContext:context];
            [object setValue:key forKey:self.keyProperty];
        }
        [fullList addObject:object];
    }

    if ([needFetch count]) {
        NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:self.entity.name];
        r.predicate = [NSPredicate predicateWithFormat:@"SELF IN %@",needFetch];
        if([context executeFetchRequest:r error:error] == nil) {//load the missing faults from store
            fullList = nil;
        }
    }

    return fullList;
}

在此实现中,您需要自己跟踪对象的删除/创建。 Br />
成功保存后,您可以使用注册/取消注册方法(简单实现)。

您可以通过挂钩到上下文save通知和更新缓存有相关更改。

In this implementation you need to keep track of objects deletion/creation yourself.
You can use the register/unregister methods (trivial implementation) for this after a successful save.
You could make this a bit more automatic by hooking into the context "save" notification and updating the cache with relevant changes.

多线程情况要复杂得多(在考虑性能时,界面相同但实现不同) 。
例如,您必须让执行者在将新项目(到商店)保存之前将其返回到请求上下文,因为它们没有永久ID,否则即使您调用获得永久性ID,请求上下文可能不会最终保存。

您还需要使用某种类型(并行或串行)的分派队列来访问您的缓存字典。

The multi-threaded case is much more complex (same interface but different implementation altogether when taking performance into account).
For instance, you must make your enforcer save new items (to the store) before returning them to the requesting context as they don't have permanent IDs otherwise, and even if you call "obtain permanent IDs" the requesting context might not save eventually.
you will also need to use a dispatch queue of some sort (parallel or serial) to access your cache dictionary.

一些数学:

Some math:

给定:

10K(10 * 1024) >
平均键长度为256 [byte]

objectID长度为128 [byte]

我们正在查看:

10K *(256 + 128)=〜4 [MB]的内容

Given:
10K (10*1024) unique key objects
average key length of 256[byte]
objectID length of 128[byte]
we are looking at:
10K*(256+128) =~ 4[MB] of memory

这可能是一个高估计,但你应该考虑到这一点...

This might be a high estimate, but you should take this into account ...

这篇关于核心数据查找或创建最有效的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆