6

Einfach ausgedrückt, gibt es eine Möglichkeit, eine allgemeine Benachrichtigung zu erhalten, wenn eine Eigenschaft in einer Objective-C-Klasse geändert wird? Ich weiß, dass ich KVO verwenden kann, um bestimmte Eigenschaftsänderungen zu überwachen, aber ich muss eine bestimmte Methode aufrufen, wenn eine setProperty: Nachricht an meine Klasse gesendet wird. Ich möchte in der Lage sein, eine allgemeine Benachrichtigung ohne Bedenken darüber zu erhalten, welche Eigenschaft speziell geändert wurde.Eine Änderung an jeder Klasseneigenschaft in Objective-C beobachten

Wenn es hilft zu erklären, warum ich dies tun wollen, ich bin Verwendung einiger schnellen Tabellen Scrollen Code machen hier: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/

Teil des Prozesses dies zu erreichen, besteht darin, dass, wenn eine Eigenschaft in einer Tabellenansicht Zelle wird geändert, [ self setNeedsDisplay ] muss aufgerufen werden. Ich möchte lieber nicht die Setter-Methoden für jede Eigenschaft in meiner Klasse überschreiben, nur um diesen Aufruf zu machen.

Antwort

5

Wie Chuck bemerkt, können Sie einen abhängigen Schlüssel erstellen, oder natürlich können Sie alle Eigenschaften direkt beobachten (was weniger Arbeit ist als das Überladen der Setter).

Wenn Sie die Objective-C-Laufzeit verwenden und ausschließlich Eigenschaften verwenden, können Sie diesen Prozess mit class_copyPropertyList() automatisieren. Aber ich würde das wahrscheinlich nur tun, wenn dieses Problem für dich etwas aufkommt. Wenn Sie nur eine Instanz dieses Problems haben, ist es wahrscheinlich einfacher und sicherer und wartbarer, nur die Liste der Eigenschaften direkt zu beobachten, es sei denn, Sie möchten in der ObjC-Laufzeit arbeiten.

2

Nicht genau. Sie können ein dependent key erstellen, das von jeder Eigenschaft abhängt, die Sie aussetzen möchten, und das dann beachten. Das ist so nah wie möglich, denke ich.

+0

Hm. Scheint so, als würde ich in diesem Fall das Konzept der abhängigen Schlüssel missbrauchen. Ich nehme an, ich beobachte einfach jede Eigenschaft einzeln. Ich hätte schwören können, dass es einen generischen Weg dafür gab. Wie wäre es mit einer Möglichkeit, den Namen der Nachricht abzufangen, die über die Laufzeit an eine Klasse weitergegeben wird? – LucasTizma

+0

Nicht wirklich.Rufen Sie einfach den abhängigen Schlüssel 'propertiesChanged' oder etwas ähnliches auf und es wird ziemlich genau sein. MALALC bei Apple schlägt diese Technik in seinem Cocoa Bindings Beispiel und Hinweisen vor: http://homepage.mac.com/mmalc/CocoaExamples/controllers.html ... Wie "den Namen der Nachricht abzufangen, die an eine Klasse übergeben wird", I Angenommen, Sie meinen * alle * Nachrichten, die an eine Klasse gesendet werden. Dies würde entweder ein Proxy-Objekt erfordern oder sich in die 'objc_msgSend()' Laufzeitfunktion einklinken, wobei die letztere massiv langsam und hackisch wäre und es wirklich nicht wert wäre. – Chuck

4

Hier ist ein Beispiel gebaut weg von Chuck und Rob Vorschläge:

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

beobachten propertiesChanged wird uns jederzeit eine Eigenschaft wissen lassen, hat sich geändert .

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

Hier ein Beispiel für Code. Ich habe ein allgemeines Objekt und ein anderes Objekt. Dother-Objekt muss seinen Status speichern, um jede Eigenschaft zu ändern.

#import <Foundation/Foundation.h> 

@interface GeneralObject : NSObject 

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

@end 

Implementierung

#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 

und schließlich haben wir dother Klasse, die seinen Zustand auf jede Änderung von Eigentum

#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 

und Implementierung sparen sollte

#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 

Fröhliche Codierung!

Verwandte Themen