Cocoa KVO与NSUndoManager
我的文档类中有一个名为Cocoa KVO与NSUndoManager,cocoa,key-value-observing,Cocoa,Key Value Observing,我的文档类中有一个名为passDescription的属性,类型为NSMutableDictionary。调用setPassDescription:时,使用NSJSONSerialization将属性的当前值归档到NSData实例中。更新属性的支持ivar,passDescription,然后注册撤消操作。该操作调用的选择器重新构造给定给它的NSData,并调用setPassDescription: - (void)setPassDescription:(NSDictionary *)param
passDescription
的属性,类型为NSMutableDictionary
。调用setPassDescription:
时,使用NSJSONSerialization
将属性的当前值归档到NSData
实例中。更新属性的支持ivar,passDescription
,然后注册撤消操作。该操作调用的选择器重新构造给定给它的NSData
,并调用setPassDescription:
- (void)setPassDescription:(NSDictionary *)param
{
NSLog(@"passDescription (before) = \n%@", passDescription);
NSError *error;
NSData *archivedOldValue = [NSJSONSerialization dataWithJSONObject:passDescription options:0 error:&error];
NSAssert(archivedOldValue != nil, @"Could not archive old pass description: %@", error);
NSData *blob = [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:&error];
if (blob == nil) @throw [NSException exceptionWithName:@"PBJSONException" reason:@"Could not serialize pass.json" userInfo:@{ @"error": error }];
[self.fileBrowserRoot fileWrapperWithName:@"pass.json"].fileContents = blob;
[passDescriptionLock lock];
[[self.undoManager prepareWithInvocationTarget:self] setPassDescriptionFromData:archivedOldValue];
passDescription = param;
[passDescriptionLock unlock];
NSLog(@"passDescription (after) = \n%@", passDescription);
// After the pass description has been set, refresh the list of build issues.
[self recheckForIssues];
}
现在,笑话来了:passDescription
正在使用键值观察进行观察。在Xcode调试器中进行的大量实验和检查表明,旧值和新值是相同的。(我知道这不是指针别名问题,因为这就是我使用NSData
实例的原因。NSData
是在我记录新值之前创建的,使其独立于从何创建的。)因此,当我按Command-Z撤消时,由于刚刚恢复的值与被撤消覆盖的值相同,因此不会发生任何情况
我能想到的唯一可能导致这种情况的原因是,KVO在调用setPassDescription:
之前,正在为我设置passDescription
ivar。为什么会这样,我如何防止KVO这样做?(我已经确认setter没有被调用两次。如果是,我会在调试器控制台中看到两次输出。)
以下是setPassDescription:
的源代码
- (void)setPassDescription:(NSDictionary *)param
{
NSLog(@"passDescription (before) = \n%@", passDescription);
NSError *error;
NSData *archivedOldValue = [NSJSONSerialization dataWithJSONObject:passDescription options:0 error:&error];
NSAssert(archivedOldValue != nil, @"Could not archive old pass description: %@", error);
NSData *blob = [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:&error];
if (blob == nil) @throw [NSException exceptionWithName:@"PBJSONException" reason:@"Could not serialize pass.json" userInfo:@{ @"error": error }];
[self.fileBrowserRoot fileWrapperWithName:@"pass.json"].fileContents = blob;
[passDescriptionLock lock];
[[self.undoManager prepareWithInvocationTarget:self] setPassDescriptionFromData:archivedOldValue];
passDescription = param;
[passDescriptionLock unlock];
NSLog(@"passDescription (after) = \n%@", passDescription);
// After the pass description has been set, refresh the list of build issues.
[self recheckForIssues];
}
通常它不应该为您设置值。这就是二传手的作用。你能发布一些代码(也许还有更多的上下文)吗?您确定以后不会使用相同的值调用setPassDescription:吗?这可能会使用相同的数据创建一个额外的撤消操作,并且看起来旧数据是相同的。@user2311023–更新的信息