dee*_*ter 6 core-data objective-c ios restkit
我有一个视图控制器,我在其中创建一个NSFetchedResultsController以在TableView中显示一组CoreData对象.在tableview中查看这些对象时,可以通过调用RestKit getObjectsAtPath来更新它们,它通过响应描述符和RKEntityMapping正确处理,以更新CoreData对象上的字段.但是,此特定实体还具有自定义派生字段 - 实际上是状态机(基于TransitionKit),它读取提供给实体的状态值,并使用服务器提供的状态重新初始化状态机.但是,无论我在哪里重新初始化状态机(awakeFromFetch,willSave,键值观察),当NSFetchedResultsController中的对象副本用于更新相应的表格单元格时(NSFetchResultsController为)时,不会更新此重新初始化状态机.通知该行的变化).要清楚 - 通过RestKit EntityMapping IS更新的值已更新,但状态机(派生值)未更新.这是为什么?
不应该以允许它们计算派生值的方式通知NSFetchedResultsController的对象数组吗?当我在代码中跟踪时,主线程中的awakeFromFetch还没有包含更新的值,并且在willSave或setter中计算我的派生值似乎不会在NSFetchedResultsController持有的对象的实例中创建此派生值.
我已附上我的基本型号代码
#import "VCStateMachineManagedObject.h"
@interface VCStateMachineManagedObject ()
@property (nonatomic, strong) TKStateMachine * stateMachine;
@end
@implementation VCStateMachineManagedObject
@dynamic savedState;
@synthesize stateMachine = _stateMachine;
@synthesize forcedState;
-(id)init {
self = [super init];
if(self != nil) {
}
return self;
}
- (BOOL)canFireEvent:(id)eventOrEventName {
return [_stateMachine canFireEvent:eventOrEventName];
}
- (BOOL)fireEvent:(id)eventOrEventName userInfo:(NSDictionary *)userInfo error:(NSError **)error{
return [_stateMachine fireEvent:eventOrEventName userInfo:userInfo error:error];
}
- (void) assignStatesAndEvents:(TKStateMachine *) stateMachine {
[NSException raise:@"Invoked abstract method" format:@"Invoked abstract method"];
}
- (NSString *) getInitialState {
[NSException raise:@"Invoked abstract method" format:@"Invoked abstract method"];
return nil;
}
- (void)awakeFromInsert {
if(self.savedState == nil){
self.savedState = [self getInitialState];
}
[self createStateMachine];
}
- (void)awakeFromFetch {
if(self.savedState == nil){
self.savedState = [self getInitialState];
}
[self addObserver:self forKeyPath:@"savedState" options:NSKeyValueObservingOptionNew context:nil];
[self createStateMachine];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(![[_stateMachine.currentState name] isEqualToString:self.savedState]){
[self createStateMachine];
}
}
- (void) willSave {
NSLog(@"%@", self.savedState);
[self createStateMachine];
}
// Manually set the state, for restkit object mapping
- (void) setForcedState: (NSString*) state__ {
self.savedState = state__;
}
- (void) setSavedState:(NSString *)savedState__{
[self willChangeValueForKey:@"savedState"];
[self setPrimitiveValue:savedState__ forKey:@"savedState"];
[self didChangeValueForKey:@"savedState"];
[self createStateMachine];
}
- (NSString *) state {
NSString * state = [_stateMachine.currentState name];
return [NSString stringWithFormat:@"%@ %@", state, self.savedState];
}
#pragma mark - State Machine
- (void)prepareStateMachine {
for(TKEvent * event in _stateMachine.events){
[event setDidFireEventBlock:^(TKEvent *event, TKTransition *transition) {
self.savedState = transition.destinationState.name;
}];
}
}
- (void) createStateMachine {
_stateMachine = [TKStateMachine new];
[self assignStatesAndEvents:_stateMachine];
[self prepareStateMachine];
_stateMachine.initialState = [_stateMachine stateNamed:self.savedState];
[_stateMachine activate];
}
@end
Run Code Online (Sandbox Code Playgroud)
@quellish这是我在托管对象中跟踪断点时看到的内容.
在我查看状态机中的变量的所有原因中,它们已更新为正确的值.但是,当NSFetchedResultsController触发更改时,状态机即使值本身也不会更新.
在接下来的几个小时内,我将追溯到这一点,并提供有关我所看到的行为的更具体信息.我还要为实体的每个实例添加一个UUID,以确保正在更新的实例实际上是NSFetchedResultsController中的实例.敬请关注.
您在问题中提供的实现有一些问题。您的访问器和 KVO 观察实现对您不利,并且似乎干扰了基于 KVO 的 Core Data 更改跟踪。
以下是您可以改进的一些措施,可能会解决您遇到的问题。这肯定会帮助您解决一些可能尚未遇到但肯定会遇到的问题。
observeValueForKeyPath:ofObject:change:context:addObserver:forKeyPath:context:正确的实现应该根据传递给和
的已知值检查上下文指针removeObserver:forKeyPath:context:。这使您可以将自己的观察结果与其他人的观察结果区分开来。这就引出了下一点——正确的实现调用 super。如果上下文值不是您已知的值,请遵循 super。例子:
添加观察者:
[self addObserver:self forKeyPath:keyPath options:options context:(__bridge void*)self];
Run Code Online (Sandbox Code Playgroud)
移除观察者:
[self removeObserver:self forKeyPath:keyPath context:(__bridge void*)self];
Run Code Online (Sandbox Code Playgroud)
observeValueForKeyPath:ofObject:change:context:执行:
- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context {
if ((__bridge id)context == self){
// This is our observation, handle it here.
[self setStateMachine:nil];
} else {
// This is important for Core Data to work correctly.
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
}
}
Run Code Online (Sandbox Code Playgroud)
CoreData 广泛使用 KVO 来跟踪托管对象快照的更改。当将 KVO 与 Core Data 一起使用时,您应该意识到这一点,并注意不要为 Core Data 正确实现 KVO——这与其他对象略有不同。例如,对于 NSManagedObject 子类的建模属性,自动 KVO 通知默认处于关闭状态。这意味着,如果某个属性由建模属性支持,则默认情况下它不会发出外部 KVO 通知。模型中不存在的属性将会出现。
要为建模属性启用自动 KVO 通知,请使用以下模式实现类方法:
+ (BOOL) automaticallyNotifiesObserversFor<PropertyName> {
return YES;
}
Run Code Online (Sandbox Code Playgroud)
其中 是建模属性的名称(即automaticallyNotifiesObserversForSavedState)。
在您的情况下,您选择为您的建模属性实现自定义访问器。目前尚不清楚您为什么选择从您发布的代码中执行此操作(也许您在文档中看到了willSave:有关递归的可怕警告 - 您的 will/didChangeValueForKey 消息重新引入了这一点)。很少有必要为托管对象子类提供您自己的访问器实现。通常,Core Data 在运行时提供 @dynamic 属性的访问器。当它这样做时,它提供了一个具有正确内存管理和更改跟踪以及 CPU 和内存优化的实现。
原始访问器方法用于访问托管对象的属性。这本质上意味着实例变量直接由数据模型中的值支持。不建议将属性作为原始值进行访问,而且很少值得这样做。始终更喜欢属性访问器从核心数据和模型对象中获取正确的行为。
init托管对象子类。init不是指定的首字母缩写。由于您正在实现某种状态机,因此允许 KVO 管理值之间的依赖关系(即forcedState、savedState 和stateMachine 之间的依赖关系)可能是个好主意。例如,将savedState设置为forcedState的依赖keypath,这样当savedState改变时,系统就知道forcedState应该是“脏”的,需要重新计算:
+ (NSSet *)keyPathsForValuesAffectingValueForForcedState {
return [NSSet setWithObject:@"savedState"];
Run Code Online (Sandbox Code Playgroud)
}
一旦您的 KVO 实现得到修复,就可能不需要从托管对象生命周期方法更新您的状态机。如果您确实决定坚持使用生命周期方法,请查看 awakeFromSnapshotEvents: 是否更适合您的需求。
由于您似乎根本没有对状态机进行太多改变,只是重新创建它,因此您的用例非常简单。如果我正确地阅读了你的课程,那么当 KVO 告诉你情况已经改变时,你需要做的就是设置stateMachine为零。savedState如果访问时它为 nil state,请调用您的 create 方法来设置该属性。大多数瞬态属性都是以这种方式实现的。
| 归档时间: |
|
| 查看次数: |
596 次 |
| 最近记录: |