观察 Objective-C 中任何类属性的变化

发布于 2024-08-15 03:58:56 字数 500 浏览 3 评论 0原文

简而言之,当 Objective-C 类中的任何属性发生更改时,有没有办法接收一般通知?我知道我可以使用 KVO 来监视特定的属性更改,但每当任何 setProperty: 消息发送到我的类时,我都需要调用特定的方法。我希望能够收到通用通知,而不用担心具体修改了哪个属性。

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

完成此操作的部分过程是每当修改表视图单元格中的属性时,都需要调用 [ self setNeedsDisplay ] 。我不想只是为了进行此调用而重写类中每个属性的 setter 方法。

Put simply, is there a way to receive a general notification when any property in an Objective-C class is changed? I know I can use KVO to monitor particular property changes, but I have the need to call a particular method whenever any setProperty: message is sent to my class. I want to be able to receive a generic notification without any concern about which property in particular was modified.

If it helps to clarify why I want to do this, I am making use of some fast table scrolling code found here: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/

Part of the process of accomplishing this is that whenever a property in a table view cell is modified, [ self setNeedsDisplay ] needs to be called. I'd rather not have to override the setter methods for every property in my class just to make this call.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不再见 2024-08-22 03:58:56

正如 Chuck 所指出的,您可以创建一个依赖键,或者当然您可以直接观察所有属性(这比重载设置器要少一些工作)。

使用 Objective-C 运行时,如果您专门使用属性,则可以使用 class_copyPropertyList()。但我可能只会在您遇到这个问题时才这样做。如果您只有一个此问题的实例,那么直接观察属性列表可能会更容易、更安全且更易于维护,除非您想在 ObjC 运行时中工作。

As Chuck notes, you can create a dependent key, or of course you can directly observe all the properties (which is less work than overloading the setters).

Using the Objective-C runtime, if you exclusively use properties, you can automate this process using class_copyPropertyList(). But I'd probably only do this if this problem comes up a bit for you. If you only have one instance of this problem, it's probably easier and safer and more maintainable just to directly observe the list of properties unless you feel like working in the ObjC runtime.

我的痛♀有谁懂 2024-08-22 03:58:56

下面是根据 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];

Here's an example built off of Chuck and Rob's suggestions:

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];
}

observing propertiesChanged will let us know anytime a property has changed.

[self.drakeObject addObserver:self
                   forKeyPath:@"propertiesChanged"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
挖个坑埋了你 2024-08-22 03:58:56

不完全是。您可以创建依赖键这取决于您希望公开然后观察的每个属性。我想这就是你能得到的最接近的结果了。

Not exactly. You can create a dependent key that depends on every property you wish to expose and then observe that. That's about as close as you'll get, I think.

差↓一点笑了 2024-08-22 03:58:56

这是代码示例。我有一个一般对象和另一个对象。 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

,毕竟我们有 dother 类,它应该在任何属性的每次更改

#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

保存他的状态快乐编码!

Here an example of code. I have a general object and dother object. Dother object has to save his state on change each property.

#import <Foundation/Foundation.h>

@interface GeneralObject : NSObject

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

@end

implementation

#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

and after all we have dother class, which should save his state on each change of any property

#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

and implementation

#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

Happy coding!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文