Objective c 核心数据找到或创建最有效的方法
我有大约10000个实体“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:来尝试设置和保
Message
”的对象。当我添加一条新的“消息”
”时,我想先看看它是否存在——如果它只是更新了它的数据,但如果它不存在,我就创建它
现在,“查找或创建”算法通过将所有消息对象“objectID
”保存在一个数组中,然后对它们进行过滤,并获取带有existingObjectWithID:error:
这很好,但在我的例子中,当我使用ID为的现有ObjectWithId获取“Message”时,然后通过设置“Message”对象的属性并在其上下文上调用save:来尝试设置和保存属性时,它不会正确保存该属性。有人遇到过这样的问题吗
有没有更有效的方法来创建查找或创建算法?好的,很多事情可能会出错。以下是如何:
最重要的是,您使用相同的上下文进行获取和保存,并且您必须在同一线程中执行此操作,因为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]内存