Why does some stuff get updated immediately and some doesnt?
我已经为此苦苦挣扎了几个小时和几天。当发生更改时,我的视图控制器中的某些内容会立即更新,而有些则不会。举个例子,我会尽量复制尽可能多的代码让你理解。
我正在为我的公司开发预算应用。三个实体是:
编辑和添加事务发生在
这里是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #pragma mark Model initialization - (void)setSpendingCategory:(SpendingCategory *)spendingCategory { _spendingCategory = spendingCategory; [self setupFetchedResultsController]; self.title = NSLocalizedString(@"Transactions", nil); } - (SpendingCategory *)spendingCategory { return _spendingCategory; } - (void)setupFetchedResultsController { NSDate *startDate = [NSDate startDateForCostPeriod:[self.spendingCategory getBiggestCostPeriod]]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Expenditures"]; request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"date" ascending:NO]]; request.predicate = [NSPredicate predicateWithFormat:@"forSpendingCategory = %@ AND date >=%@", self.spendingCategory, startDate]; self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request managedObjectContext:self.spendingCategory.managedObjectContext sectionNameKeyPath:nil cacheName:nil]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self updateBudgetAndBudgetLeft]; [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:NO]; } - (void)updateBudgetAndBudgetLeft { self.budgetLabel.text = [NSString stringWithFormat:@"%@/%@",[self.spendingCategory convertCostToBiggestCostPeriodAsString], [self.spendingCategory getBiggestCostPeriodAsString]]; double moneySpent = [[self.spendingCategory getExpendituresAmountForCostPeriod:[self.spendingCategory getBiggestCostPeriod]] doubleValue]; double budget = [[self.spendingCategory convertCostToBiggestCostPeriod] doubleValue]; //Money spent will be ADDED because negative values are already stored as negative double budgetLeftCalc = budget + moneySpent; if(budgetLeftCalc >= 0){ NSNumber *budgetLeft = [NSNumber numberWithDouble:budgetLeftCalc]; self.budgetLeftLabel.text = [NSString stringWithFormat:@"%@ %@", [budgetLeft getLocalizedCurrencyStringWithDigits:0], NSLocalizedString(@"left", nil)]; } else { NSNumber *budgetLeft = [NSNumber numberWithDouble:-budgetLeftCalc]; self.budgetLeftLabel.text = [NSString stringWithFormat:@"%@ %@", [budgetLeft getLocalizedCurrencyStringWithDigits:0], NSLocalizedString(@"over", nil)]; self.budgetLeftLabel.textColor = [UIColor redColor]; } if (moneySpent < 0) { self.progressBar.progress = -moneySpent/budget; } else { self.progressBar.progress = 0; } } |
从数据库中获取每期成本的函数是:(而且这个函数显然没有及时得到正确的结果):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | - (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod { NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Expenditures"]; [fetchRequest setResultType:NSDictionaryResultType]; NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]]; fetchRequest.predicate = [NSPredicate predicateWithFormat:@"forSpendingCategory = %@ AND date >= %@", self, startDate];; //Define what we want NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"amount"]; NSExpression *sumExpression = [NSExpression expressionForFunction: @"sum:" arguments: [NSArray arrayWithObject:keyPathExpression]]; //Defining the result type (name etc.) NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init]; [expressionDescription setName: @"totalExpenditures"]; [expressionDescription setExpression: sumExpression]; [expressionDescription setExpressionResultType: NSDoubleAttributeType]; // Set the request's properties to fetch just the property represented by the expressions. [fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]]; // Execute the fetch. NSError *error = nil; NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; if (objects == nil) { return [NSNumber numberWithDouble:0]; } else { if ([objects count] > 0) { return [[objects objectAtIndex:0] valueForKey:@"totalExpenditures"]; } else { return [NSNumber numberWithDouble:0]; } } } |
如果你点击一个交易,你会进入
如果您现在更改某些事务或插入新事务,此
编辑 2:
Complete
pragma mark - 获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | - (void)performFetch { if (self.fetchedResultsController) { if (self.fetchedResultsController.fetchRequest.predicate) { if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate); } else { if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName); } NSError *error; [self.fetchedResultsController performFetch:&error]; if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]); } else { if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); } [self.tableView reloadData]; } - (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc { NSFetchedResultsController *oldfrc = _fetchedResultsController; if (newfrc != oldfrc) { _fetchedResultsController = newfrc; newfrc.delegate = self; if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) { self.title = newfrc.fetchRequest.entity.name; } if (newfrc) { if (self.debug) NSLog(@"[%@ %@] %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? @"updated" : @"set"); [self performFetch]; } else { if (self.debug) NSLog(@"[%@ %@] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); [self.tableView reloadData]; } } } #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[self.fetchedResultsController sections] count]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { return [[[self.fetchedResultsController sections] objectAtIndex:section] name]; } - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index { return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index]; } - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { return [self.fetchedResultsController sectionIndexTitles]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { return nil; } #pragma mark - NSFetchedResultsControllerDelegate - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { if(!self.reordering){ if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) { [self.tableView beginUpdates]; self.beganUpdates = YES; } } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { if(!self.reordering){ if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } } } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { if(!self.reordering){ if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeMove: [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if(!self.reordering){ if (self.beganUpdates) [self.tableView endUpdates]; } } - (void)endSuspensionOfUpdatesDueToContextChanges { _suspendAutomaticTrackingOfChangesInManagedObjectContext = NO; } - (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend { if (suspend) { _suspendAutomaticTrackingOfChangesInManagedObjectContext = YES; } else { [self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0]; } } -(void)stopSuspendAutomaticTrackingOfChangesInManagedObjectContextWithDelay:(double)delay { //the delay is necessary in my app after row editing [self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:delay]; } |
我会尝试给出一些提示,但这里的逻辑很难理解,因为代码太多。
首先,你告诉过
其次,在
1 2 3 4 5 6 | - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { if(!self.reordering){ if (self.beganUpdates) [self.tableView endUpdates]; } } |
您错过了检查
第三,为了隔离问题,我建议省略
基于这些假设,我想说的是应该显式调用
例如,我的建议是在
编辑 1
you missed to check
self.suspendAutomaticTrackingOfChangesInManagedObjectContext bool
value. What do you mean here?
如果您已暂停更改跟踪,则不应调用
So you're suggestion is to overwrite this controllerDidChangeContent
in every view controller (which is subclassing CoreDataViewController)
and call updateBudgetAndBudgetLeft there?
是的。这可能是一个选择。同样调用
Why isn't it sufficient to call this method in viewWillAppear? I mean
it should be called each time the view gets visible or not?
这取决于您何时重新计算数据。如果您执行它并且控制器保持可见,则不会调用
编辑 2
根据文档并基于这个较新的问题 iOS FetchRequest 关于聚合函数:How to include pending changes?,如果
轻松完成