0

我是目标 C 的新手,有些奇怪的东西我不明白。

我什至如何调用对象NSString上的方法NSDate?例如:

NSString* ptr = [[NSString alloc] init];
[ptr uppercaseString];
NSDate* dPtr = [[NSDate alloc] init];
[dPtr uppercaseString];
id temp;
[temp uppercaseString];

好吧,我确实知道 id 可以指向任何东西,但是它怎么知道uppercaseString没有强制转换或其他东西的方法的存在呢?

我有 C++ 和 Java 背景,我以前没有注意到这样的事情。

我很想得到一个解释。

4

4 回答 4

3

与 Java 和 C++ 不同,Objective-C 具有弱类型和后期绑定,这说明您不必进行强制转换。

这是面向对象编程的一大分界线:语言是否使用强类型,因此变量只能保存对给定类及其子类的对象的引用(或指针),或者它是否可以保存任何东西。如果变量可以保存任何对象,则必须在运行时收到消息时解析确切的方法实现。

Objective-C 从 Smalltalk 获得了后期绑定的理念(参见),但正在朝着越来越严格的类型语言发展(正式协议、id不鼓励使用类型等)。但是,基本原理保持不变。

这也是原因之一,与 C++ 不同,Objective-C 需要运行时才能在您的机器上运行。必须处理这些方法查找。

于 2013-05-25T09:06:39.990 回答
2

因为不是在调用之前检查方法是否存在,而是在尝试查找方法时进行。实际发生的事情(简化了很多)

[obj methodCall];

=> 替换 =>objc_send(obj, @"methodCall")

在 C 函数内部 objc_send 调用本身被解析并进行

If(obj.respondsTo(methodCall) Then obj.methodCall();

于 2013-05-25T09:08:46.443 回答
0

Objective-C 方法与 Java 或 C++ 方法不同。它们是消息,它们独立于任何类或对象而存在。当您在 Photo.h 中编写(取自CocoaDevCentral)时:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {
    NSString* caption;
    NSString* photographer;
}

- caption;
- photographer;

@end

你是说这个Photo类有一个标题和一个摄影师对象,它会响应消息captionphotographer. 那是为这两个项目编写代码的旧的 pre-properties 方式。

您将编写代码来Photo.m给出这两条消息的实现,以便 aPhoto可以响应它们。但是没有什么能阻止你发送caption到任何对象。这就像一部关于我们对狗说什么以及它们听到什么的老远方卡通片。任何错误都会在运行时发生。

那么,当您向不知道如何响应的对象发送消息时会发生什么?如果你没有做任何特别的事情,

  1. 运行时系统将消息打包成一个类型的事物SEL
  2. doesNotRecognizeSelector:使用该选择器将消息发送到对象。
  3. 该对象继承自NSObject引发NSInvalidArgumentException.

但是,在此之前有一些机会可以通过覆盖方法进行干预:

  • + (BOOL) resolveInstanceMethod:(SEL)aSEL

这使您可以在运行时安装实现。

  • - (id)forwardingTargetForSelector:(SEL)aSelector

这使您可以指定另一个对象来接受该消息。

  • - (void)forwardInvocation:(NSInvocation *)anInvocation

这使您可以以任何方式处理消息。

在 Objective-C 获得块之前,有许多库使用转发进行函数式编程。假设您有一个都理解该消息的NSArrayof s。假设您想收集另一个帐户中所有帐户的余额。该库没有循环,而是提供了一个带有消息的类别,您可以编写:AccountbalanceNSArrayNSArraycollect

NSArray *accounts = ...;
NSArray *balances = [[accounts collect] balance];

的结果[accounts collect]没有balance消息的实现;怎么可能? collect由图书馆提供。相反,它有一个forwardInvocation:实现,将balance消息发送给 的所有成员,并从中accounts创建一个新成员。NSArray如今,人们可能会使用积木enumerateObjectsUsingBlock:,但这是一种非常简洁而强大的技术。

于 2013-05-25T09:33:47.377 回答
0

其他人提供了答案 - 后期绑定,在运行时在对象上查找方法而不关心对象的类型 - 如果它有适当的方法,它就会被调用。

但是,您上面的调用[dPtr uppercaseString]应该会从 Xcode 产生错误。虽然编译器会执行大量检查并拒绝编译某些程序(例如上面的),但这些程序实际上是您获得的所有类型检查,并且可以轻松绕过(例如[(id)dPtr uppercaseString],将删除错误并让您的代码运行- 当它由于没有这种方法而立即出现故障时NSDate)。

本质上,类型是注释,如果您正确使用它们,您的代码应该是类型正确的,但您的代码编译时不需要类型正确性。

于 2013-05-25T09:45:28.530 回答