我從服務器檢索數據,我需要處理它。使用GCD和核心數據導致崩潰
對於每個密鑰,我創建一個NSManagedObject
。每個對象都在相同的上下文中創建。我正在使用魔法記錄。
-(id)init {
if (self = [super init]){
self.context = [NSManagedObjectContext MR_contextForCurrentThread];
}
return self;
}
線程:
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
for (id key in boundariesDictionary) {
dispatch_group_async(group, queue, ^{
DLog(@"nsthread: %@", [NSThread currentThread]);
NSString *boundaryIDString;
if ([key isKindOfClass:[NSString class]]) {
boundaryIDString = key;
}
else if ([key isKindOfClass:[NSNumber class]]) {
boundaryIDString = [key stringValue];
}
if (boundaryIDString) {
DLog(@"boundaryIDString: %@", boundaryIDString)
NSDictionary *boundaryDictionary = [boundariesDictionary objectForKey:key];
Boundary *boundary = [Boundary MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"boundaryID == %@ AND api == %@", [NSNumber numberWithInteger:[boundaryIDString integerValue]], self.serverCall.API] inContext:self.context];
if ([boundaryDictionary objectForKey:AVI_NAME]) {
if (boundary == nil) {
DLog(@"creating boundary %@", boundaryIDString);
boundary = [Boundary MR_createInContext:self.context];
boundary.boundaryID = [NSNumber numberWithInteger:[boundaryIDString integerValue]];
}
}
boundary = [self processBoundary:boundary fromBoundaryDictionary:boundaryDictionary];
}
}
}
[自processBoundary]只需要字典並將其設置爲管理對象的屬性。
if ([boundaryDictionary objectForKey:@"name"]) {
boundary.name = [boundaryDictionary objectForKey:@"name"];
}
//more data processing
這雖然導致錯誤:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x1776f7f0> was mutated while being enumerated.'
,如果我不使用相同的上下文爲每個線程運行正常。
我不明白什麼設置其他NSDictionary
boundaryDictionary,我列舉通過。我根本不會突變boundariesDictionary,只會將數據複製到核心數據中。
當我PO對象(本例中爲0x1776f7f0)時,我得到一個集合中的Boundary
對象列表。那些Boundary
對象只會存在於NSManagedObjectContext
「集合」中,我不會將它們添加到NSArray
,NSDictionary
或NSSet
。但我不認爲我列舉了這一套。我通過創建新的邊界對象來改變它。
我覺得有些事情我不明白,或者完全沒有把握。
任何想法?
UPDATE:
for (id key in boundariesDictionary) {
NSString *boundaryIDString;
if ([key isKindOfClass:[NSString class]]) {
boundaryIDString = key;
}
else if ([key isKindOfClass:[NSNumber class]]) {
boundaryIDString = [key stringValue];
}
if (boundaryIDString) {
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
DLog(@"saveWithBlock thread: %@", [NSThread currentThread]);
NSDictionary *boundaryDictionary = [boundariesDictionary objectForKey:key];
Boundary *boundary = [Boundary MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"boundaryID == %@ AND api == %@", [NSNumber numberWithInteger:[boundaryIDString integerValue]], self.serverCall.API] inContext:localContext];
if ([boundaryDictionary objectForKey:AVI_NAME]) {
if (boundary == nil) {
boundary = [Boundary MR_createInContext:localContext];
boundary.boundaryID = [NSNumber numberWithInteger:[boundaryIDString integerValue]];
}
boundary = [self processBoundary:boundary fromBoundaryDictionary:boundaryDictionary];
if (boundary == nil) {
//Prompt Error
}
else {
for (NSNumber *groupID in groupIDs) {
if ([groupID isKindOfClass:[NSNumber class]]) {
Group *group = [Group MR_findFirstWithPredicate:[NSPredicate predicateWithFormat:@"groupID == %@ OR groupID == 0 AND api == %@", groupID, self.serverCall.API]];
if (group != nil) {
group.lastUpdated = [NSDate date];
[group addBoundariesObject:boundary];
}
else {
DLog(@"group %@ DNE", groupID);
}
}
}
}
}
} completion:^(BOOL success, NSError *error) {
DLog(@"saveWithBlock completion Block | time: %f", [[NSDate date] timeIntervalSinceDate:startTime]);
}];
}
}
所以我Group
29應該看到所有我創建了邊界,但它不是。它不一致。有時會看到所有,有時會看到一些,有時看不到。
而且,我經常看到
NO CHANGES IN ** BACKGROUND SAVING (ROOT) ** CONTEXT - NOT SAVING
在日誌中。它與我看到的這些消息的數量也不一致,而保存的上下文將插入多於一個的對象。
不知道它是否應該如何表現,似乎應該是1比1的比例,如果每個塊都有自己的上下文,並且每個塊只創建1個對象。
我記錄每個線程ID,並且它爲每個塊創建一個新的線程。沒有線程ID被記錄兩次,所以線程不應被重用。
魔法記錄有助於減少樣板編碼並有助於穿線。它可以讓你創建上下文並保存回所有新上下文產生的主上下文。它有助於使核心數據更容易線程安全。在這一點上,我還沒有保存它。只是試圖在同一個環境下創建它們。 – Padin215
@ Log139我知道什麼是魔法記錄,它不會對你的代碼進行任何有用的幫助。它使用的代碼不是線程安全的。你無法在CoreData上合法地做你想做的事情。 CoreData需要您連續訪問上下文。 –
不,核心數據當然可以與線程和隊列一起使用,如果您遵循一個簡單的規則,即您的託管對象和上下文不應該跨越線程邊界。這裏的問題是gcd隊列不是線程。他們重用線程,所以你可能違反了這個簡單的規則,將你的上下文綁定到線程並重用它。 – casademora