我有大约10000个实体'Message
'的对象。当我添加一个新的“Message
”时,我想首先查看它是否存在 - 以及它是否只是更新它的数据,但是如果它不创建它。
现在,“查找或创建”算法可以将所有Message对象“objectID
”保存在一个数组中,然后通过它们进行过滤并获取带有existingObjectWithID:error:
的消息
这很好但在我的情况下,当我使用existingObjectWithID
获取'消息'时:然后尝试通过设置'Message'对象的属性并调用save:on it的上下文来设置和保存属性它没有正确保存它。有没有人遇到这样的问题?
是否有更有效的方法来制作查找或创建算法?
答案 0 :(得分:11)
首先,Message
是"坏"苹果在内部使用它时CoreData实体的名称,它会在开发后期引起问题
您可以阅读更多相关内容HERE
我注意到这里所有建议的解决方案都使用数组或获取请求
您可能想要考虑基于字典的解决方案......
在单线程/上下文应用程序中,通过向新插入的对象(类型为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;
}
在此实现中,您需要自己跟踪对象的删除/创建
成功保存后,您可以使用注册/取消注册方法(简单实现)
您可以通过挂钩上下文来更加自动化#34; save&#34;通过相关更改通知和更新缓存。
多线程案例要复杂得多(同样的界面,但考虑到性能时,完全不同的实现) 例如,您必须让执行者在将新项目退回到请求上下文之前将其保存到商店(因为他们没有永久ID),即使您打电话给#34;获取永久ID&#34;请求上下文最终可能无法保存 您还需要使用某种派遣队列(并行或串行)来访问缓存字典。
一些数学:
:
10K(10 * 1024)个唯一键对象
平均密钥长度为256 [字节]
objectID长度为128 [byte]
我们正在看:
10K *(256 + 128)=〜4 [MB]的内存
这可能是一个很高的估计,但你应该考虑到这一点......
答案 1 :(得分:1)
好的,很多事情都可能出错,这就是:
最重要的是你使用相同的上下文来获取和保存,你必须在同一个线程中执行它,因为MOC不是线程安全的,这是人们最常犯的错误
答案 2 :(得分:1)
目前你说你维护一个`objectID'数组。如果您需要:
过滤它们并使用
获取消息existingObjectWithID:error:
然后你需要检查你收到的消息:
这是非常低效的。这是低效的,因为您始终将对象从数据存储中取回到内存中。您也是单独进行(不进行批处理)。这基本上是你可能做到的最慢的方式。
为什么未正确保存对该对象的更改尚不清楚。你应该得到某种错误。但是,你应该真正改变你的搜索方法:
不使用循环和加载,而是使用带谓词的单个提取请求:
NSFetchRequest *request = ...;
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"XXX == %@", YYY];
[request setPredicate:filterPredicate];
[request setFetchLimit:1];
其中XXX
是要测试的邮件中的属性名称,YYY
是要对其进行测试的值。
当您在MOC上执行此提取时,您应该得到一个或零响应。如果您得到零,则创建+插入新消息并保存MOC。如果您得到一个,请更新它并保存MOC。