【发布时间】:2014-08-19 06:19:56
【问题描述】:
我有一个视图控制器,我在其中创建了一个 NSFetchedResultsController 以在 TableView 中显示一组 CoreData 对象。在 tableview 中查看这些对象时,它们通过调用 RestKit getObjectsAtPath 进行更新,该方法通过响应描述符和 RKEntityMapping 正确处理以更新 CoreData 对象上的字段。但是,这个特定实体也有一个自定义派生字段 - 实际上是一个状态机(基于 TransitionKit),它读取提供给实体的状态值并使用服务器提供的状态重新初始化状态机。但是,无论我在哪里重新初始化状态机(awakeFromFetch,willSave,key-value observing),当NSFetchedResultsController中的对象副本用于更新对应的表格单元格时(当NSFetchResultsController为通知该行的更改)。需要明确的是 - 通过 RestKit EntityMapping 更新的值已更新,但状态机(派生值)未更新。为什么会这样?
不应该以允许它们计算派生值的方式通知 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
@quellish 这是我在托管对象中跟踪断点时看到的内容。
- 我呼吁 restkit 下载新对象
- Restkit 在后台线程(不是主线程)中下载对象。我看到它找到匹配的对象(awakeFromFetch),更新状态(setForcedState),并保存(willSave),在这个对象实例上,我看到createStateMachine方法被调用了几次(这是因为我在所有这些函数中都有它,尽管这当然对 NSFetchedResultsController 中的实例没有影响)。
- 然后我看到主线程上的一个对象被提取 (awakeFromFetch) 并经过相同的过程。
- 然后我看到主线程上的一个对象被 KVO 触发并再次通过 createStateMachine 方法。
当我在状态机中查看变量时,所有原因都已将它们更新为正确的值。但是然后,当 NSFetchedResultsController 触发更改时,即使值本身是,状态机也不会更新。
在接下来的几个小时内,我将对此进行追溯,并提供有关我所看到的行为的更具体信息。我还将为实体的每个实例添加一个 UUID,以确保正在更新的那个实际上是 NSFetchedResultsController 中的那个。敬请期待。
【问题讨论】:
-
KVO 是否为密钥而被解雇?是否“只是”时间问题(因此 FRC 在 KVO 通知到达 MO 本身之前更新)?
-
这是我想不通的奇怪事情之一。是的,KVO 被触发了,我可以通过我的 observeValueForKeyPath: 方法跟踪它,看起来一切都会好起来的。但是在视图控制器中,当对象被读取时,状态机仍然没有更新的值(虽然 savedState 有)。
-
为什么选择使用
setPrimitiveValue:forKey:?看起来您的实现可能会破坏 CoreData 的错误和关键值观察。 -
我将其切换为 setPrimitive,因为以正常方式执行此操作不起作用。改回来对行为没有影响,正如我提到的,KVO 和 awakeFromFetch 似乎是在主线程上调用的。
-
当您使用带有 performBlock 或 performBlockAndWait 的私有上下文时,它们会在主线程上被调用? performBlockAndWait 几乎总是从调用线程执行,但不应用于修改托管对象的故障状态的任何事情。
标签: ios objective-c core-data restkit