问题
I thinks there is a problem of controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
, specifically for NSFetchedResultsChangeUpdate
: It does not tell you what change causes this NSFetchedResultsChangeUpdate
.
I have a very complex entity. Only a part of its data is are used to populate my table view cells. I don't want to update my cells for any irrelevant property changes — it is a waste of resources. So how can I know which property change causes this NSFetchedResultsChangeUpdate
so that I can avoid updating my table view cells if it is irrelevant?
回答1:
NSManagedObject have two methods for that. First returns a dictionary with the keys and (new) values that have been changed since last fetching or saving the object (this is implemented efficiently without firing relationship faults)
- (NSDictionary *)changedValues;
Second:
- (NSDictionary *)changedValuesForCurrentEvent NS_AVAILABLE(10_7, 5_0);
You call their on NSFetchedResultsChangeUpdate notification by NSFetchedResultsController
回答2:
You can compare the values assigned to the cell with the value of the properties in the NSManagedObject that changed. Let's say that you are using 3 labels on your custom cell, and you have an update on your object, you can add a method to your cell subclass and pass the NSManagedObject asking the cell if it requires an update. The cell will verify the values of the NSManagedObject and compare them with the values of the label. If they are not equal, it requires an update. You can access the cell from the indexpath of the delegate method you posted on your question.
回答3:
Apple achieve this optimisation (in MobileNotes) by making a UITableViewCell subclass that retains the managed object (good KVO practice) and in a custom setter it adds KVO for the properties of interest, but first removes KVO for the previous object if there was one. In prepareForReuse it sets the object to nil. In dealloc it removes KVO if _object is not nil.
Here is my generic demo class, however rather than subclassing, it would be better to copy the code into your class; renaming the object property:
MCDManagedObjectTableViewCell.h
#import <CoreData/CoreData.h>
#import <UIKit/UIKit.h>
#import <MCoreData/MCDDefines.h>
NS_ASSUME_NONNULL_BEGIN
MCDATA_EXTERN void * const MCDManagedObjectTableViewCellKVOContext;
@interface MCDManagedObjectTableViewCell<__covariant ManagedObjectType : __kindof NSManagedObject *> : UITableViewCell
// needs to be retained to prevent being turned into a fault
@property (nullable, strong, nonatomic) ManagedObjectType object;
// overrides
// update views from the object's properties
- (void)updateViewsFromCurrentObject NS_REQUIRES_SUPER;
// calls update if on screen otherwise sets a flag and then updates when comes on screen.
- (void)updateViewsFromCurrentObjectIfNecessary NS_REQUIRES_SUPER;
// use addObserver and the MCDManagedObjectTableViewCellKVOContext
- (void)addKVOObserversForObject:(ManagedObjectType)object;
- (void)removeKVOObserversForObject:(ManagedObjectType)object;
@end
NS_ASSUME_NONNULL_END
MCDManagedObjectTableViewCell.m
#import "MCDManagedObjectTableViewCell.h"
void * const MCDManagedObjectTableViewCellKVOContext = (void *)&MCDManagedObjectTableViewCellKVOContext;
@interface MCDManagedObjectTableViewCell ()
@property (nonatomic) BOOL needsToUpdateViews;
@end
@implementation MCDManagedObjectTableViewCell
- (void)setObject:(__kindof NSManagedObject *)object{
if(_object == object){
return;
}
else if(_object){
[self removeKVOObserversForObject:_object];
}
_object = object;
if(object){
[self addKVOObserversForObject:object];
}
[self updateViewsFromCurrentObjectIfNecessary];
}
- (void)addKVOObserversForObject:(__kindof NSManagedObject *)object{
}
- (void)removeKVOObserversForObject:(__kindof NSManagedObject *)object{
}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context{
if(context != MCDManagedObjectTableViewCellKVOContext){
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
[self updateViewsFromCurrentObjectIfNecessary];
}
- (void)updateViewsFromCurrentObject{
self.needsToUpdateViews = NO;
}
- (void)updateViewsFromCurrentObjectIfNecessary{
if(self.window){
[self updateViewsFromCurrentObject];
}else{
self.needsToUpdateViews = YES;
}
}
- (void)willMoveToWindow:(UIWindow *)window{
if(window && self.needsToUpdateViews){
[self updateViewsFromCurrentObject];
}
}
- (void)prepareForReuse{
[super prepareForReuse];
self.object = nil;
}
- (void)dealloc{
if(_object){
[self removeKVOObserversForObject:_object];
}
}
@end
Furthermore, if the cell needs to display a string that depends on multiple properties then a custom property, e.g. titleForTableViewCell, can be added to the model subclass and can use the 'keys affected by' mechanism and use KVO on that single property.
来源:https://stackoverflow.com/questions/11869520/how-to-know-which-property-is-changed-for-nsfetchedresultschangeupdate