ios – 手动调用viewWillAppear()时出错(Objective-C)

我试图更新我的iOS应用程序的背景下的核心数据,我通过首先删除核心数据,然后将其添加回来。 但是,我需要某些function运行的时间段,但是当我尝试在后台执行所有操作时,这些function从不运行,除非我更改页面并返回到此页面。

所以我试图通过手动调用viewWillAppear()来解决这个错误,但是我得到以下错误。

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x17191c00 of class CardScanView was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x16d99c30> ( <NSKeyValueObservance 0x16dcdf20: Observer: 0x17191c00, Key path: verifyingCard, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x16d5db30> 

发生错误的类中的方法:

 - (void) resetDatabase { count++; dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ ConDAO *con = [[ConDAO alloc] init]; DatabaseManager *manager = [DatabaseManager sharedManager]; NSError * error; NSURL * storeURL = [[[manager managedObjectContext] persistentStoreCoordinator] URLForPersistentStore:[[[[manager managedObjectContext] persistentStoreCoordinator] persistentStores] lastObject]]; [[manager managedObjectContext] reset];//to drop pending changes if ([[[manager managedObjectContext] persistentStoreCoordinator] removePersistentStore:[[[[manager 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 [[[manager managedObjectContext] persistentStoreCoordinator] addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];//recreates the persistent store } NSLog(@"*****************************"); NSLog(@"updating"); NSLog(@"count: %d", count); NSLog(@"*****************************"); [self populateDatabase:0 con:con]; NSTimer *timer = [NSTimer timerWithTimeInterval:60.0 target:self selector:@selector(resetDatabase) userInfo:nil repeats:NO]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; dispatch_async(dispatch_get_main_queue(), ^(void){ CardScanView *card = [[CardScanView alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:card selector:@selector(viewWillAppear:) name:@"updated" object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:@"updated" object:nil]; }); }); } 

viewWillAppear和viewDidDissapear在其他类中:

 - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; // Setup KVO for verifyingcard [self addObserver:self forKeyPath:@"verifyingCard" options:NSKeyValueObservingOptionNew context:nil]; if([BluetoothTech isEqualToString:@"BLE"]){ self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey: @YES}]; } else if([BluetoothTech isEqualToString:@"HID"]){ [self.bluetoothScanTextView becomeFirstResponder]; } [self loadStudents]; } - (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; // Disconnect the bluetooth peripheral device if it exists if(self.discoveredPeripheral != nil){ [self.centralManager cancelPeripheralConnection:self.discoveredPeripheral]; } // Remove KVO for verifyingCard [self removeObserver:self forKeyPath:@"verifyingCard"]; } 

什么导致错误,也有更好的方法来处理这个,而不是手动调用viewDidLoad? 谢谢

  1. 核心数据不是线程安全的。 你不能从任何线程访问任何上下文。 您应该将persistentStoreCoordinatorviewContext视为只读,并且只从主线程读取它。 核心数据的所有更改都应该通过performBackgroundTask并使用传递给它的上下文。
  2. 你不能只删除核心数据下的文件,并期望工作。 首先,在删除文件时,还有许多其他managedObjectContext正在读取或写入。 可以将第二个核心数据设置为使用外部存储来存储将存储在单独文件中的一些实体。 第三,iOS使用WAL模式进行SQLite日志logging,并且可能有(可能较大的)日记文件围绕任何Core Data持久性存储。
  3. 要更新用于核心数据更改的UI,应该使用NSFetchedResultsController。 请确保在核心数据设置中设置persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES
  4. 不要保留任何指向managedObjects的指针。 他们可以从上下文中删除,而不必知道,然后访问它们将导致崩溃。 而是使用fetchedResultsController – 即使只有一个对象。

要删除核心数据中的所有实体:

 [self.persistentContainer performBackgroundTask:^(NSManagedObjectContext * _Nonnull) { NSArray* entities = context.persistentStoreCoordinator.managedObjectModel.entities; for (NSEntityDescription* entity in entities) { NSFetchRequest* request = [NSFetchRequest fetchRequestWithEntityName:entity.name]; request.predicate = [NSPredicate predicateWithValue:YES]; request.returnsObjectsAsFaults = YES; NSArray* result = [context executeFetchRequest:request error:NULL]; for (NSManagedObject* i in result) { [context deleteObject:i]; } } [context save:NULL]; }]; 

你不应该自己调用viewDidLoadviewWillAppear等方法。 只有当你重写这些方法时,你才应该叫它们super

提取你正在运行的代码更新(看起来像你已经在loadStudents ),并调用该方法,而不是viewWillAppear

请参阅https://developer.apple.com/documentation/uikit/uiviewcontroller