在我的应用程序中,我正在删除(或尝试删除)来自两个核心数据存储的所有记录,然后再添加新的记录。它们是2个简单的存储,包含与地址簿中的记录相关的数据(VIContacts包含联系人ID和vcard散列(整数),VIGroup包含组ID和组名)。
要删除商店中的所有联系人,我会在名为-clear:
的方法中使用这段代码:
NSArray *allOldRowsInVIContacts = [[mainContext fetchObjectsForEntityName:[VIContact name]
includePropertyValues:NO
withPredicate:nil] copy];
for (NSManagedObject *obj in allOldRowsInVIContacts) {
@try {
[mainContext deleteObject:obj];
}
@catch (NSException *exception) {
NSLog(@"Exception Triggered: %@", exception.reason);
[NSException raise:exception.reason format:@"thrown on vicontacts."];
}
}
[allOldRowsInVIContacts release];
if (![mainContext save:error]) {
return NO;
}
NSArray *allOldRowsInVIGroups = [[mainContext fetchObjectsForEntityName:[VIGroup name]
includePropertyValues:NO
withPredicate:nil] copy];
NSLog(@"all rows in VIGroups count: %d", [allOldRowsInVIGroups count]);
for (NSManagedObject *obj in allOldRowsInVIGroups) {
@try {
[mainContext deleteObject:obj];
}
@catch (NSException *exception) {
NSLog(@"Exception Triggered: %@", exception.reason);
[NSException raise:exception.reason format:@"thrown on vigroups."];
}
}
[allOldRowsInVIGroups release];
NSLog(@"at the end of -clear: Going to save context.");
/* SAVE */
if (![mainContext save:error]) {
return NO;
}
应用程序似乎总是在VIGroup区域崩溃。
崩溃日志如下:
Crashed Thread: 5 Dispatch queue: com.apple.root.default-priority
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000
...
Thread 5 Crashed:: Dispatch queue: com.apple.root.default-priority
0 com.apple.CoreFoundation 0x00007fff82532574 __CFBasicHashRehash + 1412
1 com.apple.CoreFoundation 0x00007fff8252b41b __CFBasicHashAddValue + 75
2 com.apple.CoreFoundation 0x00007fff82531f78 CFBasicHashAddValue + 3176
3 com.apple.CoreFoundation 0x00007fff82547899 CFSetAddValue + 121
4 com.apple.CoreData 0x00007fff8520e3dc -[NSManagedObjectContext deleteObject:] + 220
5 com.andrei.AddressBookApp 0x000000010004da9a -[AddressBookFrameworkSyncHelper clear:] + 490
6 com.andrei.AddressBookApp 0x000000010004c8f9 +[AddressBookFrameworkSyncHelper saveSnapshot:] + 105
7 com.andrei.AddressBookApp 0x000000010002d417 -[SLSyncOperation main] + 2631
8 com.apple.Foundation 0x00007fff8b68dbb6 -[__NSOperationInternal start] + 684
其他信息
我用过Instruments来寻找僵尸,但都没有出现。应用程序中存在一些泄漏,但都与Core Data无关。
VIGroup与VIContact之间没有任何关系。它们是独立的独立实体。
奇怪的是,代码似乎永远不会进入@catch,因为控制台在崩溃之前没有收到任何Exception triggered: ...
消息。
错误会不时被抛出。该应用程序似乎在Lion上更稳定,但它经常在Mountain Lion和Snow Leopard上崩溃。
感谢。非常感谢任何帮助。
MOC已创建:
我创建了一个'NSOperation'('SLSyncOperation')并将其添加到'NSOperationQueue'。 SLSyncOperation
已添加到NSOperationQueue
:
[backgroundQueue setMaxConcurrentOperationCount:1];
// has a custom initializer
currentOperation = [[SLSyncOperation alloc] initWithPersistentStoreCoordinator:persistentStoreCoordinator
andDelegate:delegate
forceRemoteSync:forceSync];
[backgroundQueue addOperation:currentOperation];
这是SLSyncOperation的main
方法(继承自NSOperation):
- (void)main {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
syncProgressTracker = [SLSyncProgressTracker sharedProgressTracker];
syncProgressTracker.currentStatus = SLSyncStatusIdle;
// ... some other setup and sending notifications ...
/* Set up. */
managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
managedObjectContext = [[NSManagedObjectContext alloc] init];
// persistentStoreCoordinator is passed from the app delegate
[managedObjectContext setPersistentStoreCoordinator:persistentStoreCoordinator];
// ... continues with other logic (syncing to a server), and the end of the method is: ...
/* Tear down. */
[managedObjectContext release];
managedObjectModel = nil;
[pool drain];
}
正在使用MOC:
我在单例类中使用MOC,它是从SLSyncOperation
内调用的方法调用的。我假设在这种情况下,一切都发生在同一个线程中......?我将添加一些测试方法来检查这一点。
MOC在单件类中初始化:
+ (AddressBookFrameworkSyncHelper *)sharedHelper {
if (!_sharedAddressBookHelper) {
_sharedAddressBookHelper = [[AddressBookFrameworkSyncHelper alloc] init];
}
return _sharedAddressBookHelper;
}
- (id)init {
if (self = [super init]) {
mainContext = [(AddressBookAppAppDelegate *)[[NSApplication sharedApplication] delegate] managedObjectContext];
addressBookRef = [ABAddressBook sharedAddressBook];
// disable undo manager - uses less memory
[mainContext setUndoManager:nil];
}
return self;
}
在此之后,我正在使用MOC(mainContext
)进行保存,将其传递给其他使用它的方法等。例如
//saving
[sharedABF.mainContext save:error];
// passing it to a Core Data method
VIContact *contactToAdd = [VIContact newOrExistingContactWithID:contactID
inContext:sharedABF.mainContext
error:error];
// that method looks like this
+ (VIContact *)newOrExistingContactWithID:(NSString *)contactID inContext:(NSManagedObjectContext *)context error:(NSError **)error {
VIContact *theContact = [[context fetchObjectsForEntityName:[VIContact name]
includePropertyValues:YES
withPredicate:
@"personID == %@", contactID] lastObject];
if (theContact) {
return [theContact retain];
} else {
// no contact found with that ID, return a new one
VIContact *newContact = [[VIContact alloc] initAndInsertInContext:context];
newContact.personID = contactID;
return newContact;
}
}
// and then fetch all rows in a Core Data entity and remove them
NSArray *allOldRowsInVIContacts = [mainContext fetchObjectsForEntityName:[VIContact name]
includePropertyValues:NO
withPredicate:nil];
for (NSManagedObject *obj in allOldRowsInVIContacts) {
[mainContext deleteObject:obj];
}
if (![mainContext save:error]) {
return NO;
}
fetchObjectsForEntityName
方法取自here。
我将尝试查看是否使用您提到的那些方法从不同的线程访问该方法。希望这会有所帮助,并为您提供有关我如何使用MOC的更多信息。
我已将创建mainContext的线程命名为SLSyncOperationThread.Name set.
。在应用程序崩溃之前,我放了一个NSLog,它打印出线程的名称。它每次打印出这个帖子的名字。所以它似乎不是一个多线程问题。特别是因为应用程序每次都会在每次到达该点时崩溃。
答案 0 :(得分:6)
尝试简单地删除文件而不是对象:
- (void)emptyDatabase{
NSError * error;
// retrieve the store URL
NSURL * storeURL = [[self.managedObjectContext persistentStoreCoordinator] URLForPersistentStore:[[[self.managedObjectContext persistentStoreCoordinator] persistentStores] lastObject]];
// lock the current context
[self.managedObjectContext lock];
[self.managedObjectContext reset];//to drop pending changes
//delete the store from the current managedObjectContext
if ([[self.managedObjectContext persistentStoreCoordinator] removePersistentStore:[[[self.managedObjectContext persistentStoreCoordinator] persistentStores] lastObject] error:&error])
{
// remove the file containing the data
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
//recreate the store like in the appDelegate method
[[self.managedObjectContext persistentStoreCoordinator] addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];//recreates the persistent store
}
[self.managedObjectContext unlock];
}
答案 1 :(得分:4)
首先,你说实体不共享关系,但他们有什么关系?
其次,您正在获取分段错误,因为正在取消引用NULL指针。
第三,你有多少个NSManagedObjectContext对象,以及它们是如何被访问的?
看起来你的clear
方法是在NSOperationQueue
内调用的,但它并没有在主线程上运行。您不应该同时从多个线程访问单个MOC。
我最初的赌注(没有进一步的信息)是你从多个线程访问MOC,这是一件非常糟糕的事情。
此外,看起来你正在使用Matt Gallagher的单行提取。我认为它返回NSSet
,而不是NSArray
...在这种情况下似乎并不重要,但总是很好,以确保使用正确的类型。
修改强>
很遗憾,您没有说明如何调用clear
方法。我打赌这是你用评论取代的东西:
// ... continues with other logic (syncing to a server), and the end of the method is: ...
无论如何,我确信我的第一个赌注是正确的,而你正在使用来自不同线程的mainContext
MOC。您的SLSyncOperation在其自己的线程中运行,并且正在使用自己的MOC(它创建的MOC)。但是,在该操作中,它似乎是调用saveSnapshot
调用clear
,而mainContext
反过来使用NSOperation
,而不是创建用于mainContext
的MOC }}
mainContext
是AppDelegate拥有的MOC,用于主线程中的东西。它也在这个“其他”线程中被调用和使用。查看堆栈跟踪以获取证据。这是核心数据和线程的第1号规则。您不得允许多个线程并发访问同一个MOC。
所以,你正在转动一个单独的线程做一些工作,并创建一个本地MOC来完成工作。一切都很好。但是,您仍然可以从该线程调用明确要使用saveSnapshot
的方法(在这种情况下main
直接从您的操作mainContext
调用。
现在,您有几种选择:
将懒惰生成的MOC传递给那些方法,以便它们在适当的MOC上运行。也许你打算快照那个MOC?
如果你真的打算让这些方法在perform
MOC上运行,那么你需要确保它们在主线程中执行。我不喜欢dispatch_async(dispatch_get_main_queue(), ^{
// Now you can call the saveSnapshot and other stuff that must use
// mainContext since stuff in this block will execute on the main thread.
});
选择器,而更喜欢直接GCD。
managedObjectContext = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSMainQueueCurrencyType];
如果您正在使用主MOC,并且有任何其他线程,我强烈建议您 NOT 使用主MOC的限制并发。相反,我建议这个替代方案:
[managedObjectContext performBlock:^{
// Do anything with this MOC because its protected, and
// running on the right thread.
}];
现在,MOC可以像其他一样使用,没有代码更改(换句话说,它在主线程上使用时仍然会像限制MOC一样正常运行)。但是,它也可以更适合地从其他线程中使用:
performBlockAndWait
如有必要,您可以调用可重入的sync
(dispatch_sync 不可重入 - 它会导致死锁)。您仍然必须小心任何deadly embrace
操作以避免performBlockAndWait
,但可以从同一线程递归调用{{1}}而不会发生死锁。
答案 2 :(得分:4)
看起来您正在两个线程之间共享NSManagedObjectContext。这只是一个猜测,但考虑到线程5上发生的崩溃,这似乎很可能就是这个问题。您只能在一个线程上使用NSManagedObjectContext。我猜你在主线程上创建mainContext
,然后无论出于何种原因在后台线程上调用clear
方法。
以下是一些可能的解决方案:
您可以确保在主线程上调用clear
方法。以下是确保这种情况发生的一种方法:
-(void)clear:(id)object {
if(![[NSThread currentThread] isMainThread]) {
[self performSelectorOnMainThread:@selector(clear:) withObject:object waitUntilDone:NO];
return;
}
...
}
理论上你可以lock
然后unlock
你的NSManagedObjectContext。这是我建议的解决方案。
阅读this thread也可能对您有帮助。