Базовые данные: самый быстрый способ удалить все экземпляры объекта

355

Я использую Core Data для локального сохранения результатов вызова Web Services. Веб-сервис возвращает полную объектную модель, скажем, "Автомобили" - может быть около 2000 из них (и я не могу заставить веб-службу возвращать что-то меньшее, чем 1 или ВСЕ автомобили.

В следующий раз, когда я открою свое приложение, я хочу обновить сохраненную копию Core Data, снова вызвав веб-службу для всех автомобилей, однако для предотвращения дублирования мне необходимо сначала очистить все данные в локальном кэше.

Есть ли более быстрый способ очистить ВСЕ экземпляры определенного объекта в контексте управляемых объектов (например, все сущности типа "CAR" ), или мне нужно запросить их вызов, затем выполнить итерацию результатов для их удаления, затем сохраните?

В идеале я могу просто сказать, удалить все, где сущность Бла.

Теги:
core-data

23 ответа

676
Лучший ответ

iOS 9 и более поздние версии:

В iOS 9 добавлен новый класс под названием NSBatchDeleteRequest, который позволяет легко удалять объекты, соответствующие предикату, без необходимости загружать их все в память. Вот как вы его используете:

Swift 2

let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

do {
    try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myContext)
} catch let error as NSError {
    // TODO: handle the error
}

Objective-C

NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Car"];
NSBatchDeleteRequest *delete = [[NSBatchDeleteRequest alloc] initWithFetchRequest:request];

NSError *deleteError = nil;
[myPersistentStoreCoordinator executeRequest:delete withContext:myContext error:&deleteError];

Более подробную информацию о пакетных удалениях можно найти в разделе "What New in Core Data" от WWDC 2015 (начиная с ~ 14: 10).

iOS 8 и ранее:

Извлечь все и удалить все:

NSFetchRequest *allCars = [[NSFetchRequest alloc] init];
[allCars setEntity:[NSEntityDescription entityForName:@"Car" inManagedObjectContext:myContext]];
[allCars setIncludesPropertyValues:NO]; //only fetch the managedObjectID

NSError *error = nil;
NSArray *cars = [myContext executeFetchRequest:allCars error:&error];
[allCars release];
//error handling goes here
for (NSManagedObject *car in cars) {
  [myContext deleteObject:car];
}
NSError *saveError = nil;
[myContext save:&saveError];
//more error handling here
  • 73
    Я также настроил бы выборку только для получения NSManagedObjectID, чтобы уменьшить любые издержки от загрузки в полной структуре объекта.
  • 0
    @ Маркус +1 отличное предложение!
Показать ещё 32 комментария
30

Немного более очищен и универсален: добавьте этот метод:

- (void)deleteAllEntities:(NSString *)nameEntity
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:nameEntity];
    [fetchRequest setIncludesPropertyValues:NO]; //only fetch the managedObjectID

    NSError *error;
    NSArray *fetchedObjects = [theContext executeFetchRequest:fetchRequest error:&error];
    for (NSManagedObject *object in fetchedObjects)
    {
        [theContext deleteObject:object];
    }

    error = nil;
    [theContext save:&error];
}
22

Reset Сущность в Swift 3:

func resetAllRecords(in entity : String) // entity = Your_Entity_Name
    {

        let context = ( UIApplication.shared.delegate as! AppDelegate ).persistentContainer.viewContext
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
        do
        {
            try context.execute(deleteRequest)
            try context.save()
        }
        catch
        {
            print ("There was an error")
        }
    }
  • 1
    try context.save() не требуется. Спасибо мужчина!
16

Для Swift 2.0:

class func clearCoreData(entity:String) {
  let fetchRequest = NSFetchRequest()
  fetchRequest.entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: moc!)
  fetchRequest.includesPropertyValues = false
  do {
    if let results = try moc!.executeFetchRequest(fetchRequest) as? [NSManagedObject] {
      for result in results {
        moc!.deleteObject(result)
      }

      try moc!.save()
    }
  } catch {
    LOG.debug("failed to clear core data")
  }
}
12

Swift:

let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(entityName, inManagedObjectContext: context)
fetchRequest.includesPropertyValues = false

var error:NSError?
if let results = context.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObject] {
    for result in results {
        context.deleteObject(result)
    }

    var error:NSError?
    if context.save(&error) {
        // do something after save

    } else if let error = error {
        println(error.userInfo)
    }

} else if let error = error {
    println("error: \(error)")
}
  • 1
    Этот ответ должен быть обновлен новой обработкой ошибок try / catch
9

Это аналогичный вопрос для здесь, и кто-то предложил установить правило удаления отношений, поэтому вам нужно удалить только один объект. Поэтому, если у вас есть или может создать сущность с отношением "многие" к автомобилям и установить правило каскадирования при удалении высшей сущности, все машины также будут удалены. Это может сэкономить время обработки, так как вам не нужно выполнять шаги, связанные с загрузкой ВСЕХ автомобилей. В большем наборе данных это может быть абсолютно необходимо.

  • 1
    Я только что попробовал это в моем текущем проекте с около 600 основных объектов данных. Когда я инкапсулировал их в другой объект с каскадом, удаление заняло около 9,1 секунды. Если я использовал метод, предложенный Дейвом, для удаления потребуется около 8,7 секунд. Не заметная разница для меня.
8

Хороший ответ уже был отправлен, это только рекомендация!

Хорошим способом было бы просто добавить категорию к NSManagedObject и реализовать такой метод, как я:

Файл заголовка (например, NSManagedObject+Ext.h)

@interface NSManagedObject (Logic)

+ (void) deleteAllFromEntity:(NSString*) entityName;

@end

Файл кода: (например, NSManagedObject + Ext.m)

@implementation NSManagedObject (Logic)

+ (void) deleteAllFromEntity:(NSString *)entityName {
    NSManagedObjectContext *managedObjectContext = [AppDelegate managedObjectContext];
    NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
    [allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext]];
    [allRecords setIncludesPropertyValues:NO];
    NSError * error = nil;
    NSArray * result = [managedObjectContext executeFetchRequest:allRecords error:&error];
    for (NSManagedObject * profile in result) {
        [managedObjectContext deleteObject:profile];
    }
    NSError *saveError = nil;
    [managedObjectContext save:&saveError];
}

@end

... единственное, что вам нужно - это получить управляемый объект ObjectContext из делегата приложения или где у вас есть его;)

после этого вы можете использовать его как:

[NSManagedObject deleteAllFromEntity:@"EntityName"];

Еще одна оптимизация может заключаться в том, что вы удаляете параметр для имени сущности и получаете имя вместо этого из clazzname. это приведет к использованию:

[ClazzName deleteAllFromEntity];

более чистый impl (как категория для NSManagedObjectContext):

@implementation NSManagedObjectContext (Logic)

- (void) deleteAllFromEntity:(NSString *)entityName {
    NSFetchRequest * allRecords = [[NSFetchRequest alloc] init];
    [allRecords setEntity:[NSEntityDescription entityForName:entityName inManagedObjectContext:self]];
    [allRecords setIncludesPropertyValues:NO];
    NSError * error = nil;
    NSArray * result = [self executeFetchRequest:allRecords error:&error];
    for (NSManagedObject * profile in result) {
        [self deleteObject:profile];
    }
    NSError *saveError = nil;
    [self save:&saveError];
}

@end

Использование:

[managedObjectContext deleteAllFromEntity:@"EntityName"];
  • 1
    Извините, но [AppDelegate managedObjectContext] не обязательно является «чистой архитектурой» .. ;-)
  • 0
    Хорошо, правда. Его код выше основан на одном managedObjectContext. основной;) В многопоточном коде я обычно объединяю основной MOC делегата приложения с другими
Показать ещё 2 комментария
4

Расширение ответа Dave Delong.

Swift Version, которая также заботится о iOS 9 и предыдущих версиях. Я также рассмотрел обработку ошибок:

let appDelegate: AppDelegate = UIApplication.sharedApplication(). делегировать как! AppDelegate

    let fetchRequest = NSFetchRequest(entityName: "Car")
    if #available(iOS 9.0, *) {
        let delete = NSBatchDeleteRequest(fetchRequest: fetchRequest)
        do {
            try appDelegate.persistentStoreCoordinator.executeRequest(delete, withContext: appDelegate.managedObjectContext)
        } catch let error as NSError {
            print("Error occured while deleting: \(error)")
        }
    } else {
        // Fallback on earlier versions
        let carRequest = NSFetchRequest()
        carRequest.entity = NSEntityDescription.entityForName("Cars", inManagedObjectContext: appDelegate.managedObjectContext)
        carRequest.includesPropertyValues = false

        do {
            let cars: NSArray = try appDelegate.managedObjectContext.executeFetchRequest(carRequest)

            for car in cars {
                appDelegate.managedObjectContext.delete(car)
            }

            try appDelegate.managedObjectContext.save()

        } catch let error as NSError {
            print("Error occured while fetching or saving: \(error)")
        }
    }
  • 0
    проголосовал. IOS 9 способ удаления записей действительно AWSM.
3

Swift 3.X и Swift 4.X, простой способ. Изменить только YourTable

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "YourTable")
    fetchRequest.returnsObjectsAsFaults = false

    do
    {
        let results = try context.fetch(fetchRequest)
        for managedObject in results
        {
            let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
            context.delete(managedObjectData)
        }
    } catch let error as NSError {
        print("Detele all my data in \(entity) error : \(error) \(error.userInfo)")
    }
  • 0
    Также Вы можете использовать эту конструкцию: let fetchRequest: NSFetchRequest <NSFetchRequestResult> = YourTable.fetchRequest ()
3

iOS 10 и более поздние версии

Работает со всеми версиями. Перенесите имя объекта и выполните итерацию, чтобы удалить все записи и сохранить контекст.

func deleteData(entityToFetch: String, completion: @escaping(_ returned: Bool) ->()) {
        let context = NSManagedObjectContext()
        context = your managedObjectContext

        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityToFetch, in: context)
        fetchRequest.includesPropertyValues = false
         do {   
            let results = try context.fetch(fetchRequest) as! [NSManagedObject]
            for result in results {
                context.delete(result)
            }
            try context.save()
            completion(true)
        } catch {
            completion(false)
            print("fetch error -\(error.localizedDescription)")
        }
    }
  • 2
    Спасибо за публикацию вашего ответа. Это подходит для меня. Но вы не должны просто скопировать и вставить свой код здесь. Для CoreDataStack() неясно, каковы ваши CoreDataStack() или DataController() . Обновление будет оценено;)
  • 0
    Какой смысл в if #available(iOS 10.0, *) ?
2

Обновление Swift 4, iOS 12 и Xcode 10

100% работает только вырезать и вставлять

Просто поместите эту функцию в соответствующий класс
и вызовите эту функцию self.deleteData() в viewDidLoad()

или в любом месте или под функцией или кнопкой, так что, щелкнув по кнопке, все данные из объекта должны быть удалены и заменить "myEntity" в качестве объекта, который вы определили в своих основных данных

  func deleteData() {

  let appDel:AppDelegate = (UIApplication.shared.delegate as! AppDelegate)
  let context:NSManagedObjectContext = appDel.persistentContainer.viewContext
  let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "myEntity")
        fetchRequest.returnsObjectsAsFaults = false

        do
        {
            let results = try context.fetch(fetchRequest)
            for managedObject in results
            {
                let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
                context.delete(managedObjectData)
            }
        } catch let error as NSError {
            print("Deleted all my data in myEntity error : \(error) \(error.userInfo)")
  }
  }
  • 0
    Спасибо, но почему не работает концепция NSBatchDeleteRequest? любая идея.
  • 0
    @SureshDurishetti Вы импортировали CoreData в своем классе?
Показать ещё 3 комментария
2

Почему бы не сбросить данные, полученные с помощью существующего кеша? В противном случае он не будет "обновляться", он "начинает снова", и вы можете также удалить/удалить файл SQLLite и начать все заново (при условии, что вы также не сохраняете другие данные).

  • 1
    Плохое решение Если в базе данных Sqlite есть другие таблицы, мы, очевидно, потеряем все это. Это скорее взлом для конкретного решения и не может рассматриваться для более крупных случаев.
1

Этот код будет работать как для iOS 9, так и ниже

class func deleteAllRecords(in entity : String) // entity = Your_Entity_Name
    {

        let context = CoreDataStack.getContext() // Note:- Replace your context here with CoreDataStack.getContext()
        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
        if #available(iOS 9, *)
        {
            let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
            do
            {
                try context.execute(deleteRequest)
                try context.save()
            }
            catch
            {
                print("There was an error:\(error)")
            }
        }
        else
        {
            do{
                let deleteRequest = try context.fetch(deleteFetch)
                for anItem in deleteRequest {
                    context.delete(anItem as! NSManagedObject)
                }
            }
            catch
            {
                print("There was an error:\(error)")
            }
        }
        CoreDataStack.saveContext() // Note:- Replace your savecontext here with CoreDataStack.saveContext()
    }
1

В Swift 3.0

 func deleteAllRecords() {
        //delete all data
        let context = appDelegate.persistentContainer.viewContext

        let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "YourClassName")
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)

        do {
            try context.execute(deleteRequest)
            try context.save()
        } catch {
            print ("There was an error")
        }
    }
1

если объект содержит много записей, лучший способ подобен этому, поскольку он сохраняет память

 - (void)deleteAll:(NSManagedObjectContext *)managedObjectContext entityName:(NSString *)entityName
{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    [managedObjectContext setUndoManager:nil];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:managedObjectContext];
    [fetchRequest setEntity:entity];
    [fetchRequest setIncludesPropertyValues:NO];
    [fetchRequest setFetchLimit:100]; // you can change this number if you want
    NSError *error;
    NSArray *items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    while ([items count] > 0) {
        @autoreleasepool {
            for (NSManagedObject *item in items) {
                [managedObjectContext deleteObject:item];
            }
            if (![managedObjectContext save:&error]) {
                NSLog(@"Error deleting %@ - error:%@",self.entityName, error);
            }
        }
        items = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
    }
}
  • 1
    в iOS контексты поставляются без менеджера отмены по умолчанию.
0

в iOS 11.3 и Swift 4.1

let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
        let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest )
        batchDeleteRequest.resultType = .resultTypeCount
        do {
            let batchDeleteResult = try dataController.viewContext.execute(batchDeleteRequest) as! NSBatchDeleteResult
            print("The batch delete request has deleted \(batchDeleteResult.result!) records.")
            dataController.viewContext.reset() // reset managed object context (need it for working)
        } catch {
            let updateError = error as NSError
            print("\(updateError), \(updateError.userInfo)")
        }

вы должны вызвать сброс после выполнения. Если нет, он не будет обновляться в представлении таблицы.

0

Swift 4, iOS 10+
Статическая функция, которая может применяться для любого объекта для удаления всех его данных

protocol NSManagedObjectHelper {
}
extension NSManagedObject: NSManagedObjectHelper {
}
extension NSManagedObjectHelper where Self: NSManagedObject {
    static func removeAllObjectsInContext(_ managedContext: NSManagedObjectContext) {
        let request: NSFetchRequest = Self.fetchRequest()
        let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
        do {
            deleteRequest.resultType = .resultTypeObjectIDs//to clear objects from memory
            let result = try managedContext.execute(deleteRequest) as? NSBatchDeleteResult
            if let objectIDArray = result?.result as? [NSManagedObjectID] {
                let changes = [NSDeletedObjectsKey : objectIDArray]
                /*By calling mergeChangesFromRemoteContextSave, all of the NSManagedObjectContext instances that are referenced will be notified that the list of entities referenced with the NSManagedObjectID array have been deleted and that the objects in memory are stale. This causes the referenced NSManagedObjectContext instances to remove any objects in memory that are loaded which match the NSManagedObjectID instances in the array.*/
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedContext])
            }
            try managedContext.save()
        } catch let error {
            print(error)
        }
    }
}

"Комната" - это объект

Room.removeAllObjectsInContext(self.persistentContainer.viewContext)
0

Используйте NSBatchDeleteRequest для удаления нескольких записей. Если минимальная iOS равна 9.0. Если фоновый поток, выполните NSManagedObjectContext save else, используйте NSFetchRequest для получения записей и удаления всех записей в цикле и сохранения после удаления.

0

Решение Swift 3 с iOS 9 "NSBatchDeleteRequest" и возврат к более ранним версиям iOS, реализованным как расширение в "NSManagedObjectContext". Ссылка Apple https://developer.apple.com/library/content/featuredarticles/CoreData_Batch_Guide/BatchDeletes/BatchDeletes.html

extension NSManagedObjectContext {
    func batchDeleteEntities<T: NSManagedObject>(ofType type: T.Type) throws {
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: type.self))
        if #available(iOS 9.0, *) {
            let request = NSBatchDeleteRequest(fetchRequest: fetchRequest)
            let result = try execute(request) as? NSBatchDeleteResult
            if let objectIDArray = result?.result as? [NSManagedObjectID] {
                let changes = [NSDeletedObjectsKey: objectIDArray]
                NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
            }
        } else {
            fetchRequest.includesPropertyValues = false
            let results = try fetch(fetchRequest)
            if let actualResults = results as? [NSManagedObject], !actualResults.isEmpty {
                actualResults.forEach { delete($0) }
            }
        }
    }
}
0

iOS 9.0 и более поздние версии:

NSBatchDeleteRequest используется для удаления записей в основных данных. Он работает очень быстро и занимает меньше времени, чтобы удалить все записи из объекта. Для аргумента требуется NSFetchRequest. Если вы хотите удалить все записи из объекта, вы можете использовать его, и он работает для меня.

let manageObject:NSManagedObjectContext = appDelegateObject.managedObjectContext

let fetchRequest = NSFetchRequest(entityName: "EnityName")

let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

let persistCor:NSPersistentStoreCoordinator = appDelegateObject.persistentObject
 do {
        try persistCor.executeRequest(deleteRequest, withContext: manageObject)
        try manageObject.save()
    } catch {
        print(error?.localizedDescription)
    }
0

В Swift 2.0:

func deleteAllData(entity: String)
{
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let managedContext = appDelegate.managedObjectContext
    let fetchRequest = NSFetchRequest(entityName: entity)
    fetchRequest.returnsObjectsAsFaults = false

    do 
    {
        let results = try managedContext.executeFetchRequest(fetchRequest)
        for managedObject in results
        {
            let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
            managedContext.deleteObject(managedObjectData)
        }
    } catch let error as NSError {
        print("Detele all data in \(entity) error : \(error) \(error.userInfo)")
    }
}
0

Ответ Dave Delongs Swift 2.0 для меня грозил (в iOS 9)

Но это сработало:

let fetchRequest = NSFetchRequest(entityName: "Car")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)

    do {
        try managedObjectContext.executeRequest(deleteRequest)
        try managedObjectContext.save()
    }
    catch let error as NSError {
       // Handle error
    }
-3

Самый быстрый способ - просто переустановить приложение на своем устройстве simulator/ios.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню