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

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

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

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

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

Ответ 1

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

Ответ 2

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")
        }
    }

Ответ 3

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

- (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];
}

Ответ 4

Для 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")
  }
}

Ответ 5

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)")
}

Ответ 6

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

Ответ 7

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

Хорошим способом было бы просто добавить категорию к 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"];

Ответ 8

Обновление 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)")
    }
}

Ответ 9

Расширение ответа 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)")
        }
    }

Ответ 10

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)")
        }
    }

Ответ 11

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)")
    }

Ответ 12

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

Ответ 13

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

 - (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];
    }
}

Ответ 14

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)
    }

Ответ 15

В 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")
        }
    }

Ответ 16

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)

Ответ 17

Этот код будет работать как для 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()
    }

Ответ 18

Ответ 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
    }

Ответ 19

Решение 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) }
            }
        }
    }
}

Ответ 20

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

Ответ 21

в 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)")
        }

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

Ответ 22

    func deleteAll(entityName: String) {

    let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
    let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
    deleteRequest.resultType = .resultTypeObjectIDs
    guard let context = self.container?.viewContext
        else { print("error in deleteAll")
            return }

    do {
        let result = try context.execute(deleteRequest) as? NSBatchDeleteResult
        let objectIDArray = result?.result as? [NSManagedObjectID]
        let changes: [AnyHashable : Any] = [NSDeletedObjectsKey : objectIDArray as Any]
        NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [context])
    } catch {
        print(error.localizedDescription)
    }
}

Ответ 23

быстрая очистка всех объектов в БД:

func purgeAllData() {
    let uniqueNames = persistentContainer.managedObjectModel.entities.compactMap({ $0.name })

    uniqueNames.forEach { (name) in
      let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
       let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
         do {
        try persistentContainer.viewContext.execute(batchDeleteRequest)
      } catch {
        let nserror = error as NSError
        fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
      }
   }
 }

Ответ 24

путь ООП без каких-либо строк в качестве имен объектов Swift 3+, Xcode 10+

func batchDelete<T>(in context: NSManagedObjectContext, fetchRequest: NSFetchRequest<T>) throws {
    guard let request = fetchRequest as? NSFetchRequest<NSFetchRequestResult> else {
        throw ErrorService.defaultError
    }
    let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: request)
    do {
        try context.execute(batchDeleteRequest)
    } catch {
        throw error
    }
}

затем просто позвоните в блок do/catch

    let fetchRequest: NSFetchRequest<YourEntity> = YourEntity.fetchRequest()
    do {
        let data = try context.fetch(fetchRequest)
        if data.count > 0 {
            try self.batchDelete(in: context, fetchRequest: fetchRequest)
        }
    } catch {
        // throw error
    }

Ответ 25

В 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)")
    }
}

Ответ 26

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