6

Objective-C 使用动态绑定:即方法调用在运行时解决。

美好的。

点符号的使用实际上归结为方法调用

但是,为什么我不能做这样的事情:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


  // 拦截异常
  @尝试
  {
    @throw [ NSException
            exceptionWithName:@"名为 ME 的异常!"
            原因:@“因为我想”
            用户信息:无];
  }
  @catch( id exc ) // 指向异常对象的指针?
  {



    //NSLog(@"%@ : %@\n", exc.name, exc.reason ) ; //非法:请求成员
    // 'name' 在不是结构或联合的东西中。.
    // 如果objective-c 使用动态绑定,和点符号
    // 归结为调用 getter,然后
    // 为什么我必须在这里转换为具体类型?

    // 仅当我转换为具体类型 NSException* 时才有效
    NSException* nexc = (NSException*)exc ;
    NSLog(@"%@: %@\n", nexc.name, nexc.reason) ;



  }



  [池排水];
    返回0;
}

当我听到“动态绑定”时,我在想“所以它应该表现得像一种脚本语言”,我很惊讶与 JavaScript 这样的脚本语言相比,Objective-C 看起来多么不灵活。

4

3 回答 3

17

您混淆了运行时和编译器。运行时处理这个问题没有问题。问题是点表示法(它是语法糖)需要类型信息让编译器消除 Objective-C 对象和 C 结构之间的歧义。

如果您不使用点表示法,它可以工作:

NSLog( @"%@ : %@\n", [exc name], [exc reason]) ;

如果类型不是 id,上面将生成警告,因为编译器知道它确实知道类型并且不能保证调度会起作用,但它会编译并运行。

从根本上说,当前的问题是编译器需要知道是生成结构加载还是 Objective C 调度,换句话说,使用点表示法,它需要有足够的信息来确定对象和标量类型之间的区别。

于 2009-11-08T17:09:03.780 回答
17

动态绑定不是动态类型的同义词。C 是一种强类型语言,特别是参数或返回值的类型至关重要,并且会显着影响代码生成。

属性是专门为消除歧义而设计的。作为其中的一部分,决定不允许将点语法用于id.

具体来说,它解决了这种情况:

@interface Foo
- (short) length;
@end

@interface Bar
- (unsigned long long) length;
@end

鉴于上述在两个单独的头文件中,编译将只给出两个头文件已导入[anObject length]的警告。如果只导入了一个头文件,那么调用站点将被编译并返回在头文件中看到的类型。如果调用站点是针对其他方法的,则会返回一个非常意外的结果。

对点语法的限制消除了这种潜在的歧义。这也是您通常看不到方法的协变声明的原因。C ABI 只是不干净地支持它(话虽如此,Objective-C 在支持对象类型协变方面做得很差)。

实际上,Objective-C 开发人员很少使用该id类型。特定的类型声明使编译器能够显着改进其代码验证。

于 2009-11-08T17:16:52.347 回答
0

Objective-C 确实支持动态绑定。但是,您不能在“id”类型的对象上使用属性 - 但您可以向它发送您想要的任何消息。(这可能是当前定义/实现中的一个错误......但现在让我们把它放在一边。)

如果你做了

NSLog(@"%@ : %@", [exc name], [exc reason] ); 

那么它会起作用。请注意,您不需要在 NSLog 语句上添加换行符,因为它们都在不同的行上。

于 2009-11-08T17:08:09.943 回答