Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If I write a custom property getter method, will KVO still operate if the getter returns a value by accessing a value from another object?

Assume that I have a class with a readonly property on it.

//MyClass.h
@interface MyClass

@property (readonly) NSInteger MonitorMe;

@end

Now, let's assume the point of this property is to monitor changes of another property, within another object, and when the property is "observed" it returns a derived value by inspecting a value from the other, external object.

//MyClass.m
@implementation

@synthesize MonitorMe;
-(NSInteger) getMonitorMe
{
    return globalStaticClass.OtherNSInteger;
}

... Inits and Methods ...
@end

Now, let's assume that some where I create an instance of the MyClass object, and I want to add a KVO observer on the MonitorMe property.

//AnotherClass.m
@implementation AnotherClass.m

    @synthesize instanceOfMyClass;

    -(id)init
    {
        ...
        instanceOfMyMethod = [MyClass init];
            [MyClass addObserver: self 
                  forKeyPath: @"MonitorMe" 
                     options: NSKeyValuObservingOptionNew
                     context: nil];
        ...
    }

My question is, since the MonitorMe property only monitors the changes of values in an external object, will the observer method execute when the value of globalStaticClass.OtherNSInteger changes? Also, if the answer is yes, how is this done?

If this works, it would seem like compiler voodoo to me.

Note

I don't think it makes a difference, but I am using ARC for this implementation and I'm compiling for an iOS device. I doubt there are compilation differences between OS X and iOS for this type of question but, if it matters, I have an iOS project that requires such an implementation outlined above.

Also, the example outlined above is a very basic setup of my actual needs. It could be argued that I could/should add an observation to the globalStaticClass.OtherNSInteger value instead of the readonly property, MonitorMe. In my actual circumstance that answer is not sufficient because my readonly property is much more complex than my example.

like image 654
RLH Avatar asked May 04 '12 19:05

RLH


1 Answers

will the observer method execute when the value of globalStaticClass.OtherNSInteger changes?

No, but you can make that happen, via +keyPathsForValuesAffectingMonitorMe (or the more generic +keyPathsForValuesAffectingValueForKey:, if the "globalStaticClass" is actually a property of MyClass. See "Registering Dependent Keys" in the KVO Guide.

Here's a quick mockup:

#import <Foundation/Foundation.h>

@interface Monitored : NSObject 
@property NSInteger otherInteger;
@end

@implementation Monitored
@synthesize otherInteger;
@end

@interface Container : NSObject 
@property (readonly) NSInteger monitorMe;
@property (strong) Monitored * theMonitored;

- (void)changeMonitoredInteger;
@end

@implementation Container

@synthesize theMonitored;

+ (NSSet *)keyPathsForValuesAffectingMonitorMe {

    return [NSSet setWithObject:@"theMonitored.otherInteger"];
}

- (id) init {

    self = [super init];
    if( !self ) return nil;

    theMonitored = [[Monitored alloc] init];
    [theMonitored setOtherInteger:25];

    return self;
}

- (NSInteger)monitorMe
{
    return [[self theMonitored] otherInteger];
}

- (void)changeMonitoredInteger {

    [[self theMonitored] setOtherInteger:arc4random()];
}

@end

@interface Observer : NSObject 
@end

@implementation Observer

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    NSLog(@"Observing change in: %@ %@", keyPath, object);
}

@end

int main(int argc, const char * argv[])
{

    @autoreleasepool {

        Observer * o = [[Observer alloc] init];
        Container * c = [[Container alloc] init];

        [c addObserver:o
            forKeyPath:@"monitorMe"
               options:NSKeyValueObservingOptionNew
               context:NULL];

        [c changeMonitoredInteger];
        [c changeMonitoredInteger];

    }
    return 0;
}

P.S. Cocoa style notes: properties/variables should have lowercase initial letters, and (this is actually more important now because of ARC) don't name accessor methods to start with "get" -- that has a specific meaning in Cocoa involving passing in a buffer and getting data back by reference.

like image 188
jscs Avatar answered Nov 14 '22 21:11

jscs