简体   繁体   English

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

[英]ios - error when calling viewWillAppear() manually (Objective-C)

I'm trying to update core-data in the background of my ios app, I do this by first deleting the core-data and then adding it back. 我正在尝试在ios应用程序的后台更新核心数据,方法是先删除核心数据,然后再添加回去。 However, I need a certain segue to occur for some functions to run but when I try to do everything in the background these functions never run, unless I change the page and go back to it. 但是,我需要确定要运行的某些功能,但是当我尝试在后台执行所有操作时,这些功能将永远无法运行,除非我更改页面并返回到该页面。

So I tried to fix this error by calling viewWillAppear() manually but I get the following error. 因此,我尝试通过手动调用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>

method in class where error occurs: 发生错误的类中的方法:

- (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 and viewDidDissapear in other class: 其他类中的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"];
}

Whats causing the error, also is there a better way to approach this rather than manually calling viewDidLoad? 是什么引起错误,还有比手动调用viewDidLoad更好的方法来解决此问题吗? thanks 谢谢

  1. Core-data is not thread safe. 核心数据不是线程安全的。 You can't just access any context from any thread. 您不能只从任何线程访问任何上下文。 You should treat the viewContext of persistentStoreCoordinator as readonly and ONLY read it from the main thread. 您应该将persistentStoreCoordinatorviewContext视为只读,并且只能从主线程读取它。 All changes to core data should go through performBackgroundTask and use the context that is passed to it. 对核心数据的所有更改都应通过performBackgroundTask并使用传递给它的上下文。
  2. You can't just delete the files under core-data and expect stuff to work. 您不能只删除核心数据下的文件并期望它们起作用。 First there many be other managedObjectContext reading or writing while you are deleting the file. 首先,在删除文件时,会有许多其他ManagedObjectContext读取或写入。 Second core data can be set to use external storage for some entities that will be store in separate files. 可以将第二核心数据设置为对某些实体使用外部存储,这些实体将存储在单独的文件中。 Third, iOS uses WAL mode for SQLite journalling and there may be (potentially large) journal files sitting around for any Core Data persistent store. 第三,iOS使用WAL模式进行SQLite日志记录,并且对于任何Core Data持久性存储而言,可能都有(可能很大)日志文件。
  3. To update the UI for core data change you should use a NSFetchedResultsController. 要更新用于核心数据更改的UI,您应该使用NSFetchedResultsController。 make sure to set persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES in your core data setup. 确保在核心数据设置中设置persistentContainer.viewContext.automaticallyMergesChangesFromParent = YES
  4. do not keep any pointers to managedObjects. 不要保留任何指向managedObjects的指针。 They can be deleted from the context without you being aware and then accessing them will cause a crash. 可以在您不知情的情况下从上下文中删除它们,然后访问它们将导致崩溃。 Instead use a fetchedResultsController - even for just one object. 而是使用fetchedResultsController-即使仅用于一个对象。

To delete all entities in core data: 要删除核心数据中的所有实体:

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

You should never call the viewDidLoad , viewWillAppear , etc methods yourself. 您永远不要自己调用viewDidLoadviewWillAppear等方法。 Only when you override those methods you should call them on super . 仅当您覆盖这些方法时,才应在super上调用它们。

Extract the code you are running to update (which looks like you already have in loadStudents ) and call that method instead of viewWillAppear . 提取您要更新的代码(看起来就像您已经在loadStudents ),然后调用该方法而不是viewWillAppear

See https://developer.apple.com/documentation/uikit/uiviewcontroller 参见https://developer.apple.com/documentation/uikit/uiviewcontroller

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM