iOS Core Data dispatch_async 后台排队崩溃
iOS Core Data dispatch_async background queuing crash
我遇到了不可重现的崩溃,我不确定原因。我在后台队列中缓存图像。图像名称是 Core Data NSManagedObject 子类 CCCard 的属性。同时,我有一个集合视图也在访问这些CCCards。
这里是相关的代码和注意事项。
//CCDeckViewController.m --------------------------------------
- (void)viewDidLoad {
// This will fetch the CCCard objects from Core Data.
self.cards = [[CCDataManager shared] cards];
[self cacheImages];
}
- (void)cacheCardImages {
// Because these are NSManagedObjects, I access them in the background
// via their object ID. So pull the IDs out here.
NSArray *cardIds = [self.cards valueForKey:@"objectID"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
for (NSManagedObjectID *cardId in cardIds) {
// Fetch the actual CCCard object.
CCCard *card = (CCCard *)[[CCDataManager shared] objectWithId:cardId];
// Cache the card's image if it's not already there.
NSString *key = [card thumbnailCacheKey];
if ([self.cardImageCache objectForKey:key]) {
continue;
}
CCDiscardableImage *discardable = [CCHelper decompressedImageForPath:key size:[card thumbnailSize] tintable:[card tintable]];
[self.cardImageCache setObject:discardable forKey:key];
}
});
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CCCard *card = self.cards[indexPath.item];
// This line calls the code that crashes.
UIColor *color = [card color];
// More code that returns the cell.
}
// CCCard.m --------------------------------------
- (UIColor *)color {
// Crash happens on this line.
if (self.colorId.integerValue == 0) {
// some code
}
}
以下是我认为最相关的 Crashlytics 报告中的堆栈跟踪部分:
Thread #0: Crashed: com.apple.main-thread
EXC_BREAKPOINT 0x000000000000defe
0 CoreData 0x24c1b070 _sharedIMPL_pvfk_core + 247
1 CoreData 0x24c1b071 _sharedIMPL_pvfk_core + 248
2 myapp 0x4286f -[CCCard color] (CCCard.m:37)
3 myapp 0x40bbb -[CCDeckViewController collectionView:cellForItemAtIndexPath:] (CCDeckViewController.m:127)
Thread #4: com.apple.root.utility-qos
0 libsystem_pthread.dylib 0x2321920c RWLOCK_GETSEQ_ADDR
1 libsystem_pthread.dylib 0x23219295 pthread_rwlock_unlock + 60
2 libobjc.A.dylib 0x22cb8e01 rwlock_tt<false>::unlockRead() + 8
3 libobjc.A.dylib 0x22cb5af5 lookUpImpOrForward + 488
4 libobjc.A.dylib 0x22cb5903 _class_lookupMethodAndLoadCache3 + 34
5 libobjc.A.dylib 0x22cbbd7b _objc_msgSend_uncached + 26
6 CoreData 0x24c1d3ab _PFObjectIDFastEquals64 + 38
7 CoreFoundation 0x233eeed4 CFBasicHashFindBucket + 1820
8 CoreFoundation 0x233ee775 CFDictionaryGetValue + 116
9 CoreData 0x24c17037 _PFCMT_GetValue + 122
10 CoreData 0x24c16ec7 -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:optionalHandler:withInlineStorage:] + 58
11 CoreData 0x24c1b45d _PF_FulfillDeferredFault + 940
12 CoreData 0x24c1afcf _sharedIMPL_pvfk_core + 86
13 myapp 0x42991 -[CCCard imagePath] (CCCard.m:53)
14 myapp 0x41d5b __39-[CCDeckViewController cacheCardImages]_block_invoke (CCDeckViewController.m:295)
这令人沮丧,因为它在开发过程中从未发生过,因此无法检验任何理论。我正在尝试理解代码和崩溃报告以找出问题所在。我的猜测是它与故障有关,因为它访问 CCCard 对象的实例方法,但在尝试从中读取 属性 时崩溃。但为什么?
您违反了线程限制。
根据 Core Data 文档,NSManagedObjectContext
和与之关联的任何 NSManagedObject
只能在分配给它的队列上访问。
您的 dispatch_async
违反了该规则,需要更正。不再有可以在 dispatch_async
中使用核心数据的设计。
如果您希望处理是异步的,那么您应该生成一个私有 NSManagedObjectContext
,它是主 NSManagedObjectContext
的子级,然后对其执行 -performBlock:
。这将为您提供正确配置的后台处理。
此外,我强烈建议打开 -com.apple.CoreData.ConcurrencyDebug 1
,因为这会在开发过程中发现此类问题。
我遇到了不可重现的崩溃,我不确定原因。我在后台队列中缓存图像。图像名称是 Core Data NSManagedObject 子类 CCCard 的属性。同时,我有一个集合视图也在访问这些CCCards。
这里是相关的代码和注意事项。
//CCDeckViewController.m --------------------------------------
- (void)viewDidLoad {
// This will fetch the CCCard objects from Core Data.
self.cards = [[CCDataManager shared] cards];
[self cacheImages];
}
- (void)cacheCardImages {
// Because these are NSManagedObjects, I access them in the background
// via their object ID. So pull the IDs out here.
NSArray *cardIds = [self.cards valueForKey:@"objectID"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
for (NSManagedObjectID *cardId in cardIds) {
// Fetch the actual CCCard object.
CCCard *card = (CCCard *)[[CCDataManager shared] objectWithId:cardId];
// Cache the card's image if it's not already there.
NSString *key = [card thumbnailCacheKey];
if ([self.cardImageCache objectForKey:key]) {
continue;
}
CCDiscardableImage *discardable = [CCHelper decompressedImageForPath:key size:[card thumbnailSize] tintable:[card tintable]];
[self.cardImageCache setObject:discardable forKey:key];
}
});
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CCCard *card = self.cards[indexPath.item];
// This line calls the code that crashes.
UIColor *color = [card color];
// More code that returns the cell.
}
// CCCard.m --------------------------------------
- (UIColor *)color {
// Crash happens on this line.
if (self.colorId.integerValue == 0) {
// some code
}
}
以下是我认为最相关的 Crashlytics 报告中的堆栈跟踪部分:
Thread #0: Crashed: com.apple.main-thread
EXC_BREAKPOINT 0x000000000000defe
0 CoreData 0x24c1b070 _sharedIMPL_pvfk_core + 247
1 CoreData 0x24c1b071 _sharedIMPL_pvfk_core + 248
2 myapp 0x4286f -[CCCard color] (CCCard.m:37)
3 myapp 0x40bbb -[CCDeckViewController collectionView:cellForItemAtIndexPath:] (CCDeckViewController.m:127)
Thread #4: com.apple.root.utility-qos
0 libsystem_pthread.dylib 0x2321920c RWLOCK_GETSEQ_ADDR
1 libsystem_pthread.dylib 0x23219295 pthread_rwlock_unlock + 60
2 libobjc.A.dylib 0x22cb8e01 rwlock_tt<false>::unlockRead() + 8
3 libobjc.A.dylib 0x22cb5af5 lookUpImpOrForward + 488
4 libobjc.A.dylib 0x22cb5903 _class_lookupMethodAndLoadCache3 + 34
5 libobjc.A.dylib 0x22cbbd7b _objc_msgSend_uncached + 26
6 CoreData 0x24c1d3ab _PFObjectIDFastEquals64 + 38
7 CoreFoundation 0x233eeed4 CFBasicHashFindBucket + 1820
8 CoreFoundation 0x233ee775 CFDictionaryGetValue + 116
9 CoreData 0x24c17037 _PFCMT_GetValue + 122
10 CoreData 0x24c16ec7 -[NSManagedObjectContext(_NSInternalAdditions) _retainedObjectWithID:optionalHandler:withInlineStorage:] + 58
11 CoreData 0x24c1b45d _PF_FulfillDeferredFault + 940
12 CoreData 0x24c1afcf _sharedIMPL_pvfk_core + 86
13 myapp 0x42991 -[CCCard imagePath] (CCCard.m:53)
14 myapp 0x41d5b __39-[CCDeckViewController cacheCardImages]_block_invoke (CCDeckViewController.m:295)
这令人沮丧,因为它在开发过程中从未发生过,因此无法检验任何理论。我正在尝试理解代码和崩溃报告以找出问题所在。我的猜测是它与故障有关,因为它访问 CCCard 对象的实例方法,但在尝试从中读取 属性 时崩溃。但为什么?
您违反了线程限制。
根据 Core Data 文档,NSManagedObjectContext
和与之关联的任何 NSManagedObject
只能在分配给它的队列上访问。
您的 dispatch_async
违反了该规则,需要更正。不再有可以在 dispatch_async
中使用核心数据的设计。
如果您希望处理是异步的,那么您应该生成一个私有 NSManagedObjectContext
,它是主 NSManagedObjectContext
的子级,然后对其执行 -performBlock:
。这将为您提供正确配置的后台处理。
此外,我强烈建议打开 -com.apple.CoreData.ConcurrencyDebug 1
,因为这会在开发过程中发现此类问题。