Objective c 核心数据找到或创建最有效的方法

Objective c 核心数据找到或创建最有效的方法,objective-c,cocoa-touch,core-data,nsmanagedobject,nsmanagedobjectcontext,Objective C,Cocoa Touch,Core Data,Nsmanagedobject,Nsmanagedobjectcontext,我有大约10000个实体“Message”的对象。当我添加一条新的“消息””时,我想先看看它是否存在——如果它只是更新了它的数据,但如果它不存在,我就创建它 现在,“查找或创建”算法通过将所有消息对象“objectID”保存在一个数组中,然后对它们进行过滤,并获取带有existingObjectWithID:error: 这很好,但在我的例子中,当我使用ID为的现有ObjectWithId获取“Message”时,然后通过设置“Message”对象的属性并在其上下文上调用save:来尝试设置和保

我有大约10000个实体“
Message
”的对象。当我添加一条新的“
消息”
”时,我想先看看它是否存在——如果它只是更新了它的数据,但如果它不存在,我就创建它

现在,“查找或创建”算法通过将所有消息对象“
objectID
”保存在一个数组中,然后对它们进行过滤,并获取带有
existingObjectWithID:error:

这很好,但在我的例子中,当我使用ID为的现有ObjectWithId获取“Message”时,然后通过设置“Message”对象的属性并在其上下文上调用save:来尝试设置和保存属性时,它不会正确保存该属性。有人遇到过这样的问题吗


有没有更有效的方法来创建查找或创建算法?

好的,很多事情可能会出错。以下是如何:

  • 创建NSManagedObjectContext->MOC
  • 使用正确的实体创建NSFetchRequest
  • 创建NSPredicate并将其附加到提取请求
  • 在新创建的上下文上执行fetch请求
  • fetch请求将返回与谓词匹配的对象数组 (如果ID不同,则该数组中应该只有一个对象)
  • 将数组的第一个元素强制转换为NSManagedObject
  • 改变它的属性
  • 保存上下文

  • 最重要的是,您使用相同的上下文进行获取和保存,并且您必须在同一线程中执行此操作,因为MOC不是线程安全的,这是人们最常犯的错误。

    当前您说您维护了一个“objectID”数组。当您需要时:

    过滤它们并获取带有
    existingObjectWithID:error:

    在此之后,您需要检查您收到的消息:

  • 存在
  • 和你想要的匹配
  • 这是非常低效的。这是低效的,因为您总是将对象从数据存储区取回内存。您也可以单独执行(而不是批处理)。这基本上是你能做到的最慢的方法

    对该对象的更改未正确保存的原因尚不清楚。你应该得到某种错误。但是,您应该真正改变您的搜索方法:

    不要循环和加载,而是使用带有谓词的单个提取请求:

    NSFetchRequest *request = ...;
    NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"XXX == %@", YYY];
    
    [request setPredicate:filterPredicate];
    [request setFetchLimit:1];
    
    其中,
    XXX
    是要测试的消息中属性的名称,
    YYY
    是要测试它的值


    当您在MOC上执行此提取时,应该会得到一个或零个响应。如果得到零,则创建并插入一条新消息并保存MOC。如果您得到一个,请更新它并保存MOC。

    首先,
    消息对于CoreData实体来说是一个“坏”名称,因为苹果在内部使用它,它会在以后的开发中导致问题。
    您可以阅读更多关于它的信息

    我注意到这里建议的所有解决方案都使用数组或获取请求。
    您可能需要考虑基于字典的解决方案…

    在单线程/上下文应用程序中,通过向缓存(字典)中添加新插入的对象(类型为
    Message
    )并使用现有对象ID和键映射预填充缓存,可以实现这一点,而不会带来太多负担。

    考虑这个接口:

    @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) 在应用程序启动时,分配一个“唯一性强制器”并填充缓存:

    //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) 当您需要测试存在性时,只需使用:

    - (NSArray*) existingObjectIDsForKeys:(NSArray *)keys
    {
        return [_cache objectsForKeys:keys notFoundMarker:[NSNull null]];
    }
    
    3) 当您希望实际获取对象并创建缺少的对象时:

    - (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;
    }
    
    -(NSArray*)查找或创建:(NSArray*)键
    上下文:(NSManagedObjectContext*)上下文
    错误:(n错误*\u自动释放*)错误
    {
    NSMUTABLEARRY*fullList=[[NSMUTABLEARRY alloc]initWithCapacity:[密钥计数]];
    NSMutableArray*needFetch=[[NSMutableArray alloc]initWithCapacity:[密钥计数]];
    NSManagedObject*对象=nil;
    用于(id键输入键){
    NSManagedObjectID*oID=_缓存[key];
    如果(oID){
    object=[context objectWithID:oID];
    if([对象为故障]){
    [needFetch addObject:oID];
    }
    }否则{
    object=[NSEntityDescription insertNewObjectForEntityForName:self.entity.name
    inManagedObjectContext:context];
    [对象setValue:key-forKey:self.keyProperty];
    }
    [完整列表添加对象:对象];
    }
    如果([needFetch计数]){
    NSFetchRequest*r=[NSFetchRequest fetchRequestWithEntityName:self.entity.name];
    r、 谓词=[NSPredicate谓词格式:@“SELF IN%@”,needFetch];
    if([context executeFetchRequest:r error:error]==nil){//从存储中加载缺少的故障
    完整列表=无;
    }
    }
    返回完整列表;
    }
    
    在此实现中,您需要自己跟踪对象的删除/创建。
    成功保存后,您可以使用注册/取消注册方法(普通实现)进行此操作。
    您可以通过挂接上下文“save”通知并使用相关更改更新缓存,使其自动化一点。

    多线程的情况要复杂得多(考虑性能时,接口相同,但实现完全不同)。
    例如,在将新项目返回到请求上下文之前,您必须让实施者保存新项目(到商店),因为它们没有永久ID,否则,即使您调用“获取永久ID”,请求上下文最终也可能不会保存。
    您还需要使用某种类型的调度队列(并行或串行)来访问缓存字典

    一些数学:

    给定:
    10K(10*1024)唯一密钥对象
    平均密钥长度为256[字节]
    objectID长度为128[字节]
    我们正在看:
    10K*(256+128)=~4[MB]内存