6

In parole semplici, c'è un modo per ricevere una notifica generale quando viene modificata qualsiasi proprietà in una classe Objective-C? So che posso utilizzare KVO per monitorare particolari modifiche alle proprietà, ma ho la necessità di chiamare un metodo particolare ogni volta che viene inviato un messaggio setProperty: alla mia classe. Voglio essere in grado di ricevere una notifica generica senza alcuna preoccupazione su quale proprietà in particolare è stata modificata.Osservare una modifica a QUALSIASI proprietà di classe in Objective-C

Se aiuta a chiarire il motivo per cui voglio fare questo, sto facendo uso di un codice di scorrimento tavolo veloce trovato qui: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/

Parte del processo di realizzazione di questo è che ogni volta che una proprietà in una vista tabella cella modificata, [ self setNeedsDisplay ] deve essere chiamato. Preferirei non dover ignorare i metodi setter per ogni proprietà della mia classe solo per effettuare questa chiamata.

risposta

5

Come nota Chuck, è possibile creare una chiave dipendente o, naturalmente, è possibile osservare direttamente tutte le proprietà (che è meno lavoro di sovraccaricare i setter).

Utilizzando il runtime Objective-C, se si utilizzano esclusivamente le proprietà, è possibile automatizzare questo processo utilizzando class_copyPropertyList(). Ma probabilmente lo farei solo se questo problema si presentasse un po 'per te. Se hai solo un'istanza di questo problema, probabilmente è più facile, più sicuro e più gestibile solo per osservare direttamente l'elenco delle proprietà a meno che tu non voglia lavorare nel runtime ObjC.

2

Non esattamente. È possibile creare un dependent key che dipende da ogni proprietà che si desidera esporre e quindi osservare quello. È più vicino che puoi, penso.

+0

Hm. Sembra che io stia abusando del concetto di chiavi dipendenti in questo caso. Suppongo che osserverò ogni proprietà individualmente. Avrei giurato che c'era un modo generico per farlo. Che ne dici di un modo per catturare il nome del messaggio passato a una classe tramite il runtime? – LucasTizma

+0

Non proprio.Basta chiamare la chiave dipendente 'propertiesChanged' o qualcosa del genere e sarà abbastanza preciso. mmalc di Apple in realtà suggerisce questa tecnica nel suo esempio e suggerimenti di Cocoa Bindings: http://homepage.mac.com/mmalc/CocoaExamples/controllers.html ... Per quanto riguarda "il rilevamento del nome del messaggio passato a una classe", I supponi che intendi i messaggi * tutti * inviati a una classe. Ciò richiederebbe un oggetto proxy o l'hooking nella funzione di runtime 'objc_msgSend() ', l'ultimo dei quali sarebbe enormemente lento e hackish e in realtà non ne vale la pena. – Chuck

4

Ecco un esempio costruito al largo di suggerimenti Chuck e 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]; 
} 

osservando propertiesChanged ci farà sapere in qualsiasi momento una proprietà è cambiato .

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

Ecco un esempio di codice. Ho un oggetto generale e un oggetto da risolvere. L'oggetto Dother deve salvare il suo stato cambiando ogni proprietà.

#import <Foundation/Foundation.h> 

@interface GeneralObject : NSObject 

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

@end 

implementazione

#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 

e dopo tutto quello che hanno Daltri classe, che dovrebbe salvare il suo stato su ogni cambio di qualsiasi proprietà

#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 

e l'attuazione

#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 

Felice codifica!

Problemi correlati