How to typecast an id to a concrete class dynamica

2019-07-16 06:24发布

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.

4条回答
聊天终结者
2楼-- · 2019-07-16 07:03

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.

查看更多
3楼-- · 2019-07-16 07:16

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

查看更多
再贱就再见
4楼-- · 2019-07-16 07:20

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.

查看更多
冷血范
5楼-- · 2019-07-16 07:24
  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:

查看更多
登录 后发表回答