在Objective-C中观察对任何类属性的更改

时间:2009-12-15 21:16:00

标签: objective-c properties notifications key-value-observing

简单地说,有没有办法在Objective-C类中的任何属性发生变化时收到一般通知?我知道我可以使用KVO监视特定的属性更改,但只要有任何setProperty:消息发送到我的类,我就需要调用特定的方法。我希望能够收到通用通知,而不必担心特别修改哪个属性。

如果有助于澄清我为什么要这样做,我会使用一些快速表格滚动代码:http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/

完成此任务的部分过程是,只要修改了表视图单元格中的属性,就需要调用[ self setNeedsDisplay ]。我宁愿不必为我的班级中的每个属性覆盖setter方法,只是为了进行这个调用。

4 个答案:

答案 0 :(得分:6)

正如Chuck所说,你可以创建一个从属密钥,当然你可以直接观察所有属性(这比重载setter的工作少。)

使用Objective-C运行时,如果您专门使用属性,则可以使用class_copyPropertyList()自动执行此过程。但是,如果这个问题对你来说有点问题,我可能只会这样做。如果你只有这个问题的一个实例,那么直接观察属性列表可能更容易,更安全,更易于维护,除非你想在ObjC运行时工作。

答案 1 :(得分:4)

这是一个基于Chuck和Rob的建议的例子:

DrakeObject.h

@interface DrakeObject : NSObject

@property (nonatomic, strong) NSNumber *age;
@property (nonatomic, strong) NSNumber *money;
@property (nonatomic, strong) NSString *startPosition;
@property (nonatomic, strong) NSString *currentPosition;
@property (nonatomic, strong, readonly) id propertiesChanged;

@end

DrakeObject.m

@implementation DrakeObject

- (instancetype)init {
    self = [super init];
    if (self) {
        self.age = @25;
        self.money = @25000000;
        self.startPosition = @"bottom";
        self.currentPosition = @"here";
    }
    return self;
}

- (id)propertiesChanged {
    return nil;
}

+(NSSet *)keyPathsForValuesAffectingPropertiesChanged {
    return [NSSet setWithObjects:@"age", @"money", @"startPosition", @"currentPosition", nil];
}

观察propertiesChanged会在财产发生变化时随时通知我们。

[self.drakeObject addObserver:self
                   forKeyPath:@"propertiesChanged"
                      options:NSKeyValueObservingOptionNew
                      context:nil];

答案 2 :(得分:2)

不完全是。您可以创建一个dependent key,它取决于您希望公开的每个属性,然后观察它。我认为那就差不多了。

答案 3 :(得分:0)

这是一个代码示例。我有一个普通的对象和dother对象。 Dother对象必须在改变每个属性时保存他的状态。

#import <Foundation/Foundation.h>

@interface GeneralObject : NSObject

+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary;
- (instancetype)initWithDictionary:(NSDictionary *)aDictionary;
- (NSDictionary *)dictionaryValue;
- (NSArray *)allPropertyNames;

@end

实施

#import "GeneralObject.h"
#import <objc/runtime.h>

@implementation GeneralObject

#pragma mark - Public

+ (instancetype)instanceWithDictionary:(NSDictionary *)aDictionary {
    return [[self alloc] initWithDictionary:aDictionary];
}

- (instancetype)initWithDictionary:(NSDictionary *)aDictionary {
    aDictionary = [aDictionary clean];

    for (NSString* propName in [self allPropertyNames]) {
        [self setValue:aDictionary[propName] forKey:propName];
    }

    return self;
}

- (NSDictionary *)dictionaryValue {
    NSMutableDictionary *result = [NSMutableDictionary dictionary];
    NSArray *propertyNames = [self allPropertyNames];

    id object;
    for (NSString *key in propertyNames) {
        object = [self valueForKey:key];
        if (object) {
            [result setObject:object forKey:key];
        }
    }

    return result;
}

- (NSArray *)allPropertyNames {
    unsigned count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);

    NSMutableArray *array = [NSMutableArray array];

    unsigned i;
    for (i = 0; i < count; i++) {
        objc_property_t property = properties[i];
        NSString *name = [NSString stringWithUTF8String:property_getName(property)];
        [array addObject:name];
    }

    free(properties);

    return array;
}

@end

毕竟我们有课,这应该可以保存他的状态任何属性的每次更改

#import "GeneralObject.h"

extern NSString *const kUserDefaultsUserKey;

@interface DotherObject : GeneralObject

@property (strong, nonatomic) NSString *firstName;
@property (strong, nonatomic) NSString *lastName;
@property (strong, nonatomic) NSString *email;

@end

和实施

#import "DotherObject.h"

NSString *const kUserDefaultsUserKey = @"CurrentUserKey";

@implementation DotherObject

- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
    if (self = [super initWithDictionary:dictionary]) {
        for (NSString *key in [self allPropertyNames]) {
            [self addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
        }
    }

    return self;
}

- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context {
    NSDictionary *dict = [self dictionaryValue];
    [[NSUserDefaults standardUserDefaults] setObject:dict forKey:kUserDefaultsUserKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%@; dict:\n%@", [super     description], [self dictionaryValue]];
}

@end

快乐的编码!