Search code examples
iphoneobjective-ccocoadynamickey-value-observing

How to typecast an id to a concrete class dynamically at runtime?


I have several dataSources I use for one UIViewController. My view controller uses KeyValue Observing in order to follow the state of certain properties at runtime. When I swap dataSources, I need to stop observing those properties. The problem is, I'm not sure of the class of the dataSource at runtime, therefor something like this is not valid:

if (aDataSource != dataSource) {
    // Ensure we stop observing the existing dataSource, otherwise bad stuff can happen.
    [dataSource removeObserver:self forKeyPath:@"someKeyPath"]; // not valid, compiler doesn't know what class dataSource is.
    [dataSource release];
    dataSource = [aDataSource retain];
}

The compiler needs a concrete class in order to know the object's interface. How can I grab the class of dataSource in this particular case, and then typcast the dataSource for the removeObserver:forKeyPath: selector above? I prefer something dynamic/smarter than caching the name of the class in an NSString instance and referring to that whenever I switch. Meaning, I could always do something like:

NSString *lastDataSource = @"MyClass";
Class foo = [NSClassFromString(lastDataSource)];

Thanks.


Solution

    1. If you code like this:

      id foo = ...;
      [foo removeObserver:self forKeyPath:@"someKeyPath"];
      

      The compiler will be fine with it as objects with type id accepts any message (as long the signature is known to the compiler).

    2. Now if you have:

      id<NSObject> foo = ...;
      [foo removeObserver:self forKeyPath:@"someKeyPath"];
      

      The compiler will give you a warning:

      warning: '-removeObserver:forKeyPath:' not found in protocol

      This is because you're referring to the protocol NSObject not to the NSObject class where the KVO methods are defined.

    3. But if you have:

      NSObject* foo = ...;
      [foo removeObserver:self forKeyPath:@"someKeyPath"];
      

      That will compile fine too, as in this case you're using the class NSObject.

    Related links: