views:

765

answers:

4

Hi everyone,

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.

+1  A: 

What do you mean it is not valid? Do you get a compile error?

Objective-C supports dynamic typing for objects by default. You should be able to call any method on any object in Objective-C, even if the compiler can't guarantee from the static type that that object supports that method.

newacct
Nope, even if I conform my object of type 'id' to <NSObject>, I get warning: '-removeObserver:forKeyPath:' not found in protocol(s)
newacct's comment is correct and to the point, so don't downvote it. the warning is innocuous as long as the programmer can be sure that the method will be in place at runtime. love it or loathe it, ObjC is based on the concept known as duck-typing.
harms
So what if you don't put a protocol on it? Just "id". What do you get?
newacct
+1  A: 

I think you need to cast them to NSObject *, since that's where the KVO methods are (not in NSObject protocol).

pgb
+6  A: 
  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:

mfazekas
A: 

Just let me add that the approach you outline with ...

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

... will of course not be able to supress your compile-time warnings, since the class "foo" will only get calculated at run-time. So even though you as the programmer can plainly see from the code that "foo" will end up being the class "MyClass", this is not clear to the compiler, and so if "MyClass" has a method "myMethod:" you will still get a compiler warning if you send that message to an object declared as "foo".

I'm guessing you realise this, but it's better to make it clear why that approach won't solve your problem.

harms