在Core Data中删除/重置所有条目?

247

您知道删除Core Data中存储的所有条目的方法吗?我的架构应该保持不变;我只想将其重置为空白。


编辑

我希望以编程方式实现这一点,以便用户可以轻松点击“重置”按钮。


6
以下许多答案已经过时,请使用NSBatchDeleteRequest。https://dev59.com/knNA5IYBdhLWcg3wIqPr#31961330 - Suragch
可能是Core Data:删除实体的所有实例的最快方法的重复问题。 - TheNeil
33个回答

202

您仍然可以使用NSFileManager:removeItemAtPath:方法以编程方式删除文件。

NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];
然后,只需添加持久存储以确保重新创建。以编程方式遍历每个实体的操作较慢且容易出错。这种方式的使用场景是如果您想删除某些实体而不删除其他实体。但是,您仍然需要确保保留引用完整性,否则无法保存更改。只需删除存储并重新创建即可快速安全地完成,当然可以在运行时以编程方式完成。
对于iOS5+的更新: 随着iOS 5和OS X 10.7中外部二进制存储(allowsExternalBinaryDataStorage或Store in External Record File)的引入,仅删除由storeURLs指向的文件是不够的。 您会留下外部记录文件。 由于这些外部记录文件的命名方案不是公开的,因此我目前没有通用解决方案。– an0 May 8 '12 at 23:00

12
我知道如何正确获取storeCoordinator,但我不知道如何获取persistentStore。所以,您能否给出一个适当的示例,而不仅仅是这样:NSPersistentStore * store = ...; - Pascal Klein
11
使用[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error]更好。 - an0
3
如果您可以获取存储协调器,那么您可以通过persistentStores属性访问其所有持久化存储。 - Mihai Damian
1
随着iOS 5和OS X 10.7中的外部二进制存储引入(allowsExternalBinaryDataStorageStore in External Record File),仅删除storeURLs指向的文件是不够的。你会留下外部记录文件。由于这些外部记录文件的命名方案不是公开的,因此我尚无通用解决方案。 - an0
2
示例代码包括如何创建一个新的空存储在这里:https://dev59.com/DXE95IYBdhLWcg3wSb_P#8467628 - Joshua C. Lerner
显示剩余10条评论

142

你可以删除SQLite文件,但我选择通过逐个清除表的方法来实现:

- (void) deleteAllObjects: (NSString *) entityDescription  {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityDescription inManagedObjectContext:_managedObjectContext];
    [fetchRequest setEntity:entity];

    NSError *error;
    NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [fetchRequest release];


    for (NSManagedObject *managedObject in items) {
        [_managedObjectContext deleteObject:managedObject];
        DLog(@"%@ object deleted",entityDescription);
    }
    if (![_managedObjectContext save:&error]) {
        DLog(@"Error deleting %@ - error:%@",entityDescription,error);
    }

}

我之所以选择逐个表进行操作,是因为这样可以在编程过程中确认删除表内容是明智的,并且没有任何重要数据需要保留。

这种方法比直接删除文件慢得多,如果这种方法太慢,我会改用删除文件的方式。


很棒的解决方案。谢谢。DLog()是什么? - Michael Grinich
6
您可以在此处查看DLog的实现:http://www.cimgf.com/2009/01/24/dropping-nslog-in-release-builds/。 - Matt Long
3
这对我很有效。但为了让它更快,有没有一种方法可以使用一个命令删除某个实体的所有对象?就像在SQL中你可以做到这样,DROP TABLE entity_name。我不想删除整个SQL文件,因为我只想删除特定实体的所有对象,而不是其他实体。 - ma11hew28
8
使用NSDictionary *allEntities = _managedObjectModel.entitiesByName;获取模型中的所有实体,然后可以迭代此NSDictionary中的键以清除存储中的所有实体。 - adam0101
谢谢,这真的帮了我很多,因为当我尝试使用普通的 CoreData 删除时,数据似乎很顽固,不会被删除。谢谢。 - JCurativo
显示剩余3条评论

71

iOS 10+更新的解决方案

使用NSBatchDeleteRequest删除实体中所有对象,无需将它们加载到内存中或迭代处理。

// create the delete request for the specified entity
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = MyEntity.fetchRequest()
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

// get reference to the persistent container
let persistentContainer = (UIApplication.shared.delegate as! AppDelegate).persistentContainer

// perform the delete
do {
    try persistentContainer.viewContext.execute(deleteRequest)
} catch {
    print(error.localizedDescription)
}

这段代码已经更新为iOS 10和Swift 3。如果您需要支持iOS 9,请参见此问题

来源:


3
我会将整个代码块放在 moc.performBlockAndWait({ () -> Void in ... }) 中。 - SwiftArchitect
3
请确保您查看 为什么只有在重新启动应用程序或执行两次 NSBatchDeleteRequest 后实体才会被删除?。长话短说,如果实体已经加载到内存中,上述代码是不够的。 - mfaani

40

我写了一个clearStores方法,它会遍历每个存储库,从协调器和文件系统中删除它(出于错误处理的考虑)。

NSArray *stores = [persistentStoreCoordinator persistentStores];

for(NSPersistentStore *store in stores) {
    [persistentStoreCoordinator removePersistentStore:store error:nil];
    [[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:nil];
}

[persistentStoreCoordinator release], persistentStoreCoordinator = nil;

这个方法是在coreDataHelper类中的,它负责(除了其他事情之外)在持久存储为nil时创建它。


没有已知的选择器“persistentStores”的类方法。 - Aviram Netanel

27

我在HomeViewController类的按钮事件中删除了所有核心数据:

这篇文章帮助了我很多,所以我想做出贡献。

-(IBAction)buttonReset:(id)sender
{
    NSLog(@"buttonReset Pressed");

    //Erase the persistent store from coordinator and also file manager.
    NSPersistentStore *store = [self.persistentStoreCoordinator.persistentStores lastObject];
    NSError *error = nil;
    NSURL *storeURL = store.URL;
    [self.persistentStoreCoordinator removePersistentStore:store error:&error];
    [[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];


    NSLog(@"Data Reset");

    //Make new persistent store for future saves   (Taken From Above Answer)
    if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        // do something with the error
    }

}
注意,为了调用self.persistentStoreCoordinator,我在Home View Controller中声明了一个属性。(不必担心我用于保存和加载的managedObjectContext。)
@property (nonatomic, retain) NSManagedObjectContext        *   managedObjectContext;
@property (nonatomic, retain) NSPersistentStoreCoordinator  *   persistentStoreCoordinator;

然后在AppDelegate的ApplicationDidFinishLaunching方法下面创建HomeViewController后面我有这样一行代码:

homeViewController = [[HomeViewController alloc] initWithNibName:@"HomeViewController" bundle:nil];
homeViewController.managedObjectContext = self.managedObjectContext;
homeViewController.persistentStoreCoordinator = self.persistentStoreCoordinator;

@ayteat,这对你有用吗?对我来说不起作用,请看一下这个链接http://stackoverflow.com/questions/14646595/how-to-clear-reset-all-coredata-in-one-to-many-realationship - Ranjit
1
除了使用“AppDelegate *ad = [[UIApplication sharedApplication] delegate];”并将self替换为ad之外,这就是答案。不要复制最后两个代码片段。 - Cescy
1
为什么你没有调用managedObjectContext的reset方法?如果你有一些对managedObject的强引用呢? - Parag Bafna
@ParagBafna 您是正确的,上面的代码示例假定没有对托管对象的强引用。如果您有一些强引用,您应该考虑在managedObjectContext上调用“reset”,并取消引用任何托管对象。 - atreat
嘿,谢谢。另外,有没有办法在应用程序升级时完成这个操作?具体来说,我的要求是当我推出下一个版本的应用程序时,当用户从应用商店更新他们的应用程序时,核心数据和sqlite文件应该被删除并重新初始化为空白。我找到了一种方法来检测应用程序首次启动事件,使用NSUserDefaults中的Bool值,并在应用程序委托的didfinishLaunchingWithOptions中检查此值,但不知道如何清除所有这些内容。因为没有按钮,而且应用程序委托没有检测到我的“persistentStore”来像上面那样清除它。有帮助吗? - Tejas

21

iOS9+,Swift 2

删除所有实体中的所有对象

func clearCoreDataStore() {
    let entities = managedObjectModel.entities
    for entity in entities {
        let fetchRequest = NSFetchRequest(entityName: entity.name!)
        let deleteReqest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        do {
            try context.executeRequest(deleteReqest)
        } catch {
            print(error)
        }
    }
}

3
请确保您查看为什么条目直到应用程序重新启动或我执行两次NSBatchDeleteRequest才被删除?。长话短说,如果实体已加载到内存中,则上述代码是不够的。 - mfaani

19

MagicalRecord可以让这个过程变得非常容易。

[MyCoreDataObject MR_truncateAll];

17
这很酷,但与我的要求不符,因为我指定了一个 CoreData 的解决方案。 - Michael Grinich
8
"Active Record Fetching是核心数据解决方案。" - casademora
7
这样的回答超出了问题的范围。没有理由假设他想使用额外的框架来完成这个任务。 - jpswain
我认为这并没有回答问题。这是从一个实体中删除条目的好方法,而不是所有实体...!如何枚举模型中的所有实体并在它们上发送MR_truncateAll - fatuhoku
查看 MR_truncateAll 的源代码 - 它获取所有对象但不包括它们的属性(因为我们打算丢弃 NSMOs),然后迭代指定实体的对象并将其删除。https://github.com/magicalpanda/MagicalRecord/blob/master/MagicalRecord/Categories/NSManagedObject/NSManagedObject+MagicalRecord.m - 1in9ui5t

14

[对于寻求更新回复的悬赏问题的迟到回答]

经过查看之前的答案,

  • 获取并删除所有项目,如@Grouchal和其他人建议的那样,仍然是一种有效且有用的解决方案。如果您拥有非常大的数据存储库,则可能会很慢,但它仍然非常有效。
  • 像你和@groundhog注意到的那样,简单地删除数据存储已不再有效。即使你不使用外部二进制存储它也过时了,因为iOS 7在SQLite记事本中使用WAL模式。使用WAL模式,对于任何核心数据持久存储,都可能存在(潜在的大量)日志文件。

但是有一种不同的、类似的方法可以删除持久性存储,它确实起作用。关键是将持久存储文件放在自己的子目录中,该子目录不包含任何其他内容。不要只把它放在文档目录(或其他地方),而是为持久存储创建一个新的子目录。该目录的内容最终会成为持久存储文件、日志文件和外部二进制文件。如果您想清除整个数据存储,请删除该目录,它们都将消失。

在设置持久存储时,您可以这样做:

NSURL *storeDirectoryURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"persistent-store"];
if ([[NSFileManager defaultManager] createDirectoryAtURL:storeDirectoryURL
        withIntermediateDirectories:NO
        attributes:nil
        error:nil]) {
    NSURL *storeURL = [storeDirectoryURL URLByAppendingPathComponent:@"MyApp.sqlite"];
    // continue with storeURL as usual...
}

当你想要移除存储时,

[[NSFileManager defaultManager] removeItemAtURL:storeDirectoryURL error:nil];

这将递归地删除自定义子目录及其中的所有Core Data文件。

仅当您的持久存储不在与其他重要数据相同的文件夹中时,此方法才有效。例如文档目录,其中可能包含其他有用的东西。如果您处于这种情况,则可以通过查找要保留的文件并删除其他所有文件来达到相同的效果。类似于:

NSString *docsDirectoryPath = [[self applicationDocumentsDirectory] path];
NSArray *docsDirectoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:docsDirectoryPath error:nil];
for (NSString *docsDirectoryItem in docsDirectoryContents) {
    // Look at docsDirectoryItem. If it's something you want to keep, do nothing.
    // If it's something you don't recognize, remove it.
}

这种方法可能存在错误风险。你必须确保你知道每一个想要保留的文件,否则你可能会删除重要的数据。另一方面,你可以删除外部二进制文件,而不必知道用于存储它们的文件/目录名称。


如果你害怕wal文件,只需禁用它即可。 - onmyway133

11

这是一个清除 Core Data 的综合解决方案。

- (void)deleteAllObjectsInCoreData
{
    NSArray *allEntities = self.managedObjectModel.entities;
    for (NSEntityDescription *entityDescription in allEntities)
    {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        [fetchRequest setEntity:entityDescription];

        fetchRequest.includesPropertyValues = NO;
        fetchRequest.includesSubentities = NO;

        NSError *error;
        NSArray *items = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];

        if (error) {
                NSLog(@"Error requesting items from Core Data: %@", [error localizedDescription]);
            }

        for (NSManagedObject *managedObject in items) {
            [self.managedObjectContext deleteObject:managedObject];
        }

        if (![self.managedObjectContext save:&error]) {
            NSLog(@"Error deleting %@ - error:%@", entityDescription, [error localizedDescription]);
        }
    }  
}

10

如果您想删除所有对象但不想删除后备文件,可以使用以下方法:

- (void)deleteAllObjectsInContext:(NSManagedObjectContext *)context
                       usingModel:(NSManagedObjectModel *)model
{
    NSArray *entities = model.entities;
    for (NSEntityDescription *entityDescription in entities) {
        [self deleteAllObjectsWithEntityName:entityDescription.name
                                   inContext:context];
    }
}

- (void)deleteAllObjectsWithEntityName:(NSString *)entityName
                             inContext:(NSManagedObjectContext *)context
{
    NSFetchRequest *fetchRequest =
        [NSFetchRequest fetchRequestWithEntityName:entityName];
    fetchRequest.includesPropertyValues = NO;
    fetchRequest.includesSubentities = NO;

    NSError *error;
    NSArray *items = [context executeFetchRequest:fetchRequest error:&error];

    for (NSManagedObject *managedObject in items) {
        [context deleteObject:managedObject];
        NSLog(@"Deleted %@", entityName);
    }
}

请注意,它可能会非常缓慢(取决于您的对象图中有多少个对象)。


当应用程序更新时,如何删除旧数据(例如三个表中的数据,我想从一个表中清除数据) - Madan Mohan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接