0
votes

I have a class "Compass" which is intended to be the observer of another class "SensorA", "SensorB" oder "SensorC". The problem is I dont know the observed class before runtime. I used reflections in order to create an Instance while runtime. I don´t know if I´m practising KVO the right way when doing this.


---Another Extern Class---
Compass *aCompass= [[AnalogCompass alloc] initWithCompassName:@"ABC" andID...];

---The oberserving Compass.m Class---
- (id)initWithCompassName:(NSString *)CompassName
                     andIid:(int)Iid

                     showAnalog:(NSString *)ShowAnalog
                     showDigital:(NSString *)ShowDigital

{
    if (self = [super init])
    {

        super.iid = Iid;
        super.CompassName = CompassName;

        showAnalog=ShowAnalog;
        showDigital=ShowDigital;

        Class unknown_cls;

        unknown_cls = [[NSClassFromString(super.CompassName) alloc]init];

        [unknown_cls addObserver:self forKeyPath:showAnalog options:NSKeyValueObservingOptionNew context:NULL];
        [unknown_cls addObserver:self forKeyPath:showDigital options:NSKeyValueObservingOptionNew context:NULL];
}  
 }


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

    NSLog(@"IN?");

//             [super observeValueForKeyPath:keyPath
//   
//                         ofObject:object
//   
//                           change:change
//   
//               context:context];

}


---Example of the oberserved SensorA Class---
@interface SensorA : NSObject { 

double xPosition;
...
}

@property (assign) double depthInFeet;

- (id)initWithLineToParse:(NSArray *) fields;

@end

When I´m doing a change like self.xposition = position; in any of my observed and reflected Sensor Classes (SensorA,SensorB,SensorC), the "observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context" in my observer Compass ist not called. Im guessing it has something to do with the reflection and maybe with related restrictions of this kind of technique. Or maybe because reflecting with

unknown_cls = [[NSClassFromString(super.CompassName) alloc]init];
and not with
unknown_cls = [[NSClassFromString(super.CompassName) alloc]initWithLineToParse:array];

How to get this working for me? Is it the wrong attempt to observe like this maybe? Thanks for help.

1
Looks like the problem really is the reflection because I can´t call any functions in it. What can I do to get my instance dynamically?Fabian Steinhauer
Can I only call static methods from reflected Class instances?Fabian Steinhauer
You are going to see memory issues with the showAnalog and showDigital instance variables. You are using simple assignment to capture the argument values and that is not good enough. You need to either retain or copy the argument values to properly manage the instance variable memory. Something like showAnalog = [ShowAnalog copy];Bill Garrison

1 Answers

2
votes

I think the problem here is that you are trying to use a Class as an instance.

You have

Class unknown_cls

and you really need an instance of the unknown class to use as the target for KVO registrations.

id compass = [[NSClassFromString(CompassName) alloc] init];

Now you can use the 'compass' variable to register KVO observers.


Just to clarify my understanding of your classes and their relationships:

AnalogCompassis a class that acts as an observer of one or more Sensor's. When an instance of AnalogCompassis created, it is supposed to register itself as an observer of a namedSensorclass.

TheSensorclass declares one property that can be observed:depthInFeet

If this is an accurate representation of your two classes, your code will never work. Your AnalogCompass instance is getting no references to the Sensor instances that it is supposed to observe. You are also trying to observe a property (xposition) that has never been declared as an observable property of Sensor.

I assume that you have in your application at least one AnalogCompass instance and one Sensor instance. The AnalogCompass instance is supposed to observe changes on the Sensor instance.

To make this work using KVO, you'd need to minimally do something like this:

AnalogCompass *someCompass = ...;
Sensor *someSensor = ...;

/* Register someCompass as an observer of the 'xposition' 
property of someSensor */

[someSensor addObserver:someCompass forKeyPath:@"xposition" 
                options:0 context:NULL];

You also have to declare that the Sensor class has an observable property named 'xposition'.

 @interface Sensor : NSObject
 @property (nonatomic, assign) float xposition;
 @end

 @implementation Sensor
 @synthesize xposition;
 @end

If you want to do the KVO setup in the initializer of AnalogCompass, as your code seems to be doing above, you'd want something like this:

@interface AnalogCompass : NSObject
{
    Sensor *sensor;
}
@end


@implementation AnalogCompass

- (id) initWithSensor:(Sensor *)aSensor 
{
    self = [super init];
    if (!self) return nil;

    sensor = [aSensor retain];

    [sensor addObserver:self forKeyPath:@"xposition" 
                options:0 context:NULL];

    return self;
}

- (void) dealloc
{
    [sensor removeObserver:self forKeyPath:@"xposition"];
    [sensor release];
    [super dealloc];
}

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                         change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"xposition"])
    {
      // Do something interesting with the value.
    }

    else
    {
      /* super gets to handle it */
      [super observeValueForKeyPath:keyPath ofObject:object 
                          change:change context:context];
    }
}
@end

If you are going to have multiple kinds of Sensor classes, then you'll want to declare a common subclass (e.g. Sensor) that has the property you want to observe (e.g. xposition). You might alternately be able to define a @protocol that all Sensor classes implement, which in turn, defines the property that you want to observe.

I think you can avoid using reflection/introspection. Your application is going to have a collection of Sensors and some number of Compass objects alive at runtime. Something in your application is going to be keeping track of them (e.g. some other object or the application delegate is maintaining an NSArray or NSSet of sensors and/or compasses.

Maybe your Compass class is going to create its own internal Sensor object in its initializer? It's not entirely clear from your code what is going on. At some point, though, you are going to need one Compass object and one Sensor object in order to register a KVO between them.