lna*_*ger 17 objective-c ios observer-pattern
问题的简短版本:
我有一个具有大量声明属性的类,我想跟踪它是否有任何更改,以便当我调用save它的方法时,它不会写不需要时到数据库.如何更新的isDirty特性,而无需编写自定义的制定者对于所有声明的属性?
问题的较长版本:
假设我有一个这样的类:
@interface MyObject : NSObject
{
@property (nonatomic, retain) NSString *myString;
@property (nonatomic, assign) BOOL myBool;
// ... LOTS more properties
@property (nonatomic, assign) BOOL isDirty;
}
...
@implementation MyObject
{
@synthesize myString;
@synthesize myBool;
// ... LOTS more synthesizes :)
@synthesize isDirty;
}
Run Code Online (Sandbox Code Playgroud)
尝试1
我的第一个想法就是这样实现setValue:forKey::
- (void)setValue:(id)value forKey:(NSString *)key {
if (![key isEqualToString:@"isDirty"]) {
if ([self valueForKey:key] != value) {
if (![[self valueForKey:key] isEqual:value]) {
self.isDirty = YES;
}
}
}
[super setValue:value forKey:key];
}
Run Code Online (Sandbox Code Playgroud)
这完全有效,直到您使用setter(即myObject.myString = @"new string";)直接设置值,在这种情况下setValue:forKey:不会被调用(我不知道为什么我认为它会是,lol).
尝试2
观察自己的所有属性.
- (id)init
{
// Normal init stuff
// Start observing all properties of self
}
- (void)dealloc
{
// Stop observing all properties of self
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
// set isDirty to true
}
Run Code Online (Sandbox Code Playgroud)
我很确定这会奏效,但我认为必须有更好的方法.:)我也希望这是自动的,这样我就不必维护要监视的属性列表.我可以很容易地看到在保持这种状态时忘记将属性添加到列表中,然后试图找出为什么我的对象有时无法保存.
希望我忽略了一个更简单的解决这个问题的方法!
最终解决方案
请参阅下面的答案以获得最终解决方案.它基于Josh Caswell提供的答案,但却是一个有效的例子.
有点内省应该有帮助.在运行时函数可以给你的所有对象的属性的列表.然后,您可以用这些来告诉KVO这dirty是取决于在此列.这避免了必须手动更新属性列表的可维护性问题.需要注意的是,与任何其他涉及KVO的解决方案一样,如果直接更改ivar,您将不会收到通知 - 所有访问必须通过setter方法.
注册以观察其中self的dirty关键路径init,并添加此方法,创建并返回一个NSSet包含所有类属性的名称(@"dirty"当然除外).
#import <objc/runtime.h>
+ (NSSet *)keyPathsForValuesAffectingDirty
{
unsigned int num_props;
objc_property_t * prop_list;
prop_list = class_copyPropertyList(self, &num_props);
NSMutableSet * propSet = [NSMutableSet set];
for( unsigned int i = 0; i < num_props; i++ ){
NSString * propName = [NSString stringWithFormat:@"%s", property_getName(prop_list[i])];
if( [propName isEqualToString:@"dirty"] ){
continue;
}
[propSet addObject:propName];
}
free(prop_list);
return propSet;
}
Run Code Online (Sandbox Code Playgroud)
现在dirty只要设置了这个类的属性,就会触发观察.(请注意,超类中定义的属性不包含在该列表中.)
您可以改为使用该列表分别注册为所有名称的观察者.
| 归档时间: |
|
| 查看次数: |
2262 次 |
| 最近记录: |