【问题标题】:How to typecast an id to a concrete class dynamically at runtime?如何在运行时动态地将 id 类型转换为具体类?
【发布时间】:2009-05-22 17:43:04
【问题描述】:

我有几个用于一个 UIViewController 的数据源。我的视图控制器使用 KeyValue Observing 来跟踪运行时某些属性的状态。当我交换数据源时,我需要停止观察这些属性。问题是,我不确定运行时数据源的类,因此这样的东西是无效的:

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];
}

编译器需要一个具体的类才能知道对象的接口。在这种特殊情况下,如何获取数据源类,然后为上面的 removeObserver:forKeyPath: 选择器键入数据源?我更喜欢动态/更智能的东西,而不是在 NSString 实例中缓存类的名称并在我切换时引用它。意思是,我总是可以这样做:

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

谢谢。

【问题讨论】:

    标签: iphone objective-c cocoa dynamic key-value-observing


    【解决方案1】:
    1. 如果你这样编码:

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

      编译器会接受它,因为 id 类型的对象接受任何消息(只要编译器知道签名)。

    2. 现在如果你有:

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

      编译器会给你一个警告:

      警告:'-removeObserver:forKeyPath:' 在协议中找不到

      这是因为您指的是协议 NSObject 而不是定义 KVO 方法的 NSObject 类。

    3. 但如果你有:

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

      这也可以很好地编译,因为在本例中您使用的是 NSObject 类。

    相关链接:

    【讨论】:

      【解决方案2】:

      你是什么意思它无效?有没有编译错误?

      Objective-C 默认支持对象的动态类型。您应该能够在 Objective-C 中的任何对象上调用任何方法,即使编译器不能从静态类型保证该对象支持该方法。

      【讨论】:

      • 不,即使我将 'id' 类型的对象符合 ,我也会收到警告:'-removeObserver:forKeyPath:' not found in protocol(s)
      • newacct 的评论是正确的,切中要害,所以不要投反对票。只要程序员可以确定该方法将在运行时就位,警告是无害的。不管你喜不喜欢,ObjC 都是基于鸭式打字的概念。
      • 那如果你不把协议放在上面呢?只是“身份证”。你得到了什么?
      【解决方案3】:

      我认为您需要将它们转换为 NSObject *,因为这是 KVO 方法所在的位置(不在 NSObject 协议中)。

      【讨论】:

        【解决方案4】:

        让我补充一下您概述的方法......

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

        ... 当然不能抑制你的编译时警告,因为类“foo”只会在运行时计算。因此,即使您作为程序员可以从代码中清楚地看到“foo”最终将成为类“MyClass”,编译器也不清楚,所以如果“MyClass”有一个方法“myMethod:”,你会如果您将该消息发送到声明为“foo”的对象,仍然会收到编译器警告。

        我猜你已经意识到了这一点,但最好弄清楚为什么这种方法不能解决你的问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多