111
- 1. 从 C&C++过渡到 Objective-C
序:
对于已经熟悉 C++的人来说,从事 iPhone 开发一开始就陷入过多的 Objective-C 的细节可
能并非好事。
我们希望可以更加关注于 iPhone 开发特有的东西,快速上手。
下面这些内容可以帮助 C++程序员快速获得 Objective-C 的基本知识,并开始编写和阅读
iPhone 程序,如果在读别人的程序时,有些语法搞不懂,可以翻查我前面发的那本电子书
《 <Cocoa 入 门 --- 使 用 Objective-C> 英 文 版 》
(http://www.weiphone.com/thread-119791-1-1.html),把其作为参考手册用。
以 下 内 容 均 整 理 / 翻 译 自 《 iPhone Open Application Development 》
(http://www.weiphone.com/thread-119662-1-1.html)
编/译者: yangqiang.alan@gmail.com
1、消息
你应该注意的第一件事情就是 Objective-C 中大量使用了中括号。在 Objective-C 中,方法
不是使用传统形式(C/C++的语法形式)“调用”的;代替的是,“方法调用”变成了“消息
发送”。与此类似,“方法返回”,变成了“消息响应”。在 C 中,在调用方法之前必须预先
将其定义好;而 Objective-C 的消息风格,允许开发者在运行时动态创建方法和消息。这样
- 2. 做的负面影响就是:完全允许向一个对象发送一个不可能返回的消息,这将导致异常,进而
可能让程序崩溃。
这里有一个对象 myWidget,一个消息可以被发送到它的 powerOn 方法上,像这样:
returnValue = [ myWidget powerOn ];
如果用 C++做同样的事情,则像这样:
returnValue = myWidget->powerOn();
C 则会在其扁平的名字空间上声明一个函数:
returnValue = widget_powerOn(myWidget);
只要一个对象可以接收,就可以向其传递参数。下面的例子调用了一个 setSpeed 方法,并
传递了两个参数:
returnValue = [ myWidget setSpeed: 10.0 withMass: 33.0 ];
注意,在这个消息中,第二个参数使用了显式名称(explicitly named)。这就允许声明多个具
有相同名称和类型的方法——即多态。
returnValue = [ myWidget setSpeed: 10.0 withMass: 33.0 ];
returnValue = [ myWidget setSpeed: 10.0 withGyroscope: 10.0 ];
2、类与方法声明
尽管可以在 Objective-C 中定义 C++的类,但最好能善用 Objective-C 自己的对象和特性。
Objective-C 中有接口的概念。在标准 C++中,类是结构,而类的变量和方法是被包含在结
构中的。Objective-C 中,把类的变量放在 one part of the class 中,方法放在另一部分中。
Objective-C 要求把 interface 声明在其自己的代码块中(in its own code block),称为
@interface;而其实现则定义在@implementation 代码块中。方法自身的构造跟 Smalltalk
- 3. 很像,看其来跟 C 非常不同。
我们的组件(widget)的 interface 是下面这样的,放在一个 MyWidget.h 文件中。
#import <Foundation/Foundation.h>
@interface MyWidget : BaseWidget
{
BOOL isPoweredOn;
@private float speed;
@protected float mass;
@protected float gyroscope;
}
+ (id)alloc;
+ (BOOL)needsBatteries;
- (BOOL)powerOn;
- (void)setSpeed:(float)_speed;
- (void)setSpeed:(float)_speed withMass:(float)_mass;
- (void)setSpeed:(float)_speed withGyroscope:(float)_gyroscope;
@end
在下面的小节中会详细解释这个文件中的重要语义元素。
2.1、Import
使用预处理指令#import 代替传统的#include 指令(尽管#include 还是可以使用的)。使用
- 4. #import 的一个好处就是:它内建的逻辑可以保证相同的资源不会被包含多于一次。这就可
以避免像在 C 代码中那样,使用这样的宏:
#ifndef _MYWIDGET_H
#define _MYWIDGET_H
...
#endif
2.2、interface 声明
使用@interface 语句再跟上接口名和基类(如果有基类的话),来声明一个接口。此代码块使
用@end 语句结束。
2.3、方法
方法声明放在花括号结构的外面。+号表示这是一个静态方法,而-号声明这个方法是一个实
例方法。可以使用 MyWidget 类的引用直接调用 alloc 方法(分配一个新对象),而 MyWidget
类的实例方法(比如 needsBatteries 和 powerOn),就通过 alloc 返回的类实例来调用。
方法声明的每个参数,都使用数据类型、局部变量名和一个可选的外部变量名(external
variable name)来表示。外部变量名的例子就是上面程序中的 withMass 和 withGyroscope。
方法调用者在调用方法时,使用外部变量名;但在方法内部,则使用局部变量名来引用这个
参数。这样,setSpeed 方法就使用局部变量_mass 来获取传递进来的 withMass 的值。
如果在声明中没有提供外部变量名,则调用者只会使用一个冒号来引用这个变量,比
如 :10.0。
- 5. 3、实现
Objective-C 源文件的后缀是.m。前面小节中 widget 类的骨架实现(skeletion implementation)
如下,放在一个 MyWidget.m 文件中。
#import "MyWidget.h"
@implementation MyWidget
+ (id)alloc {
}
+ (BOOL)needsBatteries {
return YES;
}
- (BOOL)powerOn {
isPoweredOn = YES;
return YES;
}
- (void)setSpeed:(float)_speed {
speed = _speed;
}
- 6. - (void)setSpeed:(float)_speed withMass:(float)_mass {
speed = _speed;
mass = _mass;
}
- (void)setSpeed:(float)_speed withGyroscope:(float)_gyroscope {
speed = _speed;
gyroscope = _gyroscope;
}
@end
就好像接口放在它自己的代码块中一样,实现放在由@implementation 语句开始、@end
语句结束的代码块中。在 C++中,使用 m_作为成员变量名的前缀,是一同常见的做法。而
Objective-C 中,局部变量名使用下划线作为前缀,通过外部变量名,向别人提供可读的变
量名称。
4、Category
Objective-C 向面向对象编程中增加了一个新的元素:称为 category。Category 被设计用来
解决这样的问题:基类被认为是脆弱的,应该避免对其进行看起来无害的改变,因为这样可
能会破坏更加复杂的派生类。当一个程序增长到特定的规模时,开发者经常害怕去触摸那个
较小的基类,因为如果不对整个应用程序详细审查,就无法确定:对基类的改变是否安全。
- 7. Category 提供了一个机制,来为基类增加功能,而不影响其他的对象。
category 类可以增加和替换一些基类的方法。这可以在不重新编译基类、甚至不必访问基
类源代码的情况下做到。Category 允许在有限的范围内(within a limited scope)对基类进行
扩展(expanded),那么任何使用基类(而非 category 类)的对象,将继续看到原始的版本。
从开发的角度看,这使得你更容易改进其他开发者所写的类。在运行时,使用 category 的
代码部分将看到这个类的新版本,而直接使用基类的代码则只看到原始版本。
组件工厂发布了一种新型的组件,它可以在太空中飞行,但是如果改变它们的基类,将可能
破坏已经存在的应用。通过构建一个 category,使用 MyWidget 基类的应用程序将继续看见
这个原始类;而新的太空应用将使用 category。下面的例子在已经存在的 MyWidget 基类上
构建了一个新的 category,名为 MySpaceWidget。因为我们需要在太空中爆炸的能力,所
以增加了一个名为 selfDestruct 的方法。这个 category 还用自己的方法替换了已经存在的
powerOn 方法。对比一下:这里使用了圆括号包含 MySpaceWidget,来定义 category;而
在上面的例子中使用的是冒号来定义继承。
#import "MyWidget.h"
@interface MyWidget (MySpaceWidget)
- (void)selfDestruct;
- (BOOL)powerOn;
@end
下面是该 category 的实现:
#import "MySpaceWidget.h"
- 8. @implementation MyWidget (MySpaceWidget)
- (void)selfDestruct {
isPoweredOn = 0;
speed = 1000.0;
mass = 0;
}
- (BOOL)powerOn {
if (speed == 0) {
isPoweredOn = YES;
return YES;
}
/* Don't power on if the spaceship is moving */
return NO;
}
@end
5、Pose(伪装)
在 Objective-C 中,子类对象可以伪装成其父类(pose as one of its superclasses),替换父
类对象接收所有的消息。这跟 overriding(重写)很像,但 overriding 只能重写整个类,而非
- 9. 单个方法。在 Pose 类中,不能声明任何新成员变量,但可以重写或替换已经存在的方法。
Pose 跟 Category 有点相似,它们都允许开发者在运行时扩展一个已经存在的类(augment
an existing class at runtime)。
在前面的例子里,创建了一些机械组件类(mechanical widget classes)。随后,在定义了所
有这些组件之后,发现了永久能源。这就允许很多新的组件成为自治的了,然而老的组件仍
然需要电池。因为自治组件拥有非常庞大的不同代码,所以派生了一个名为
MyAutonomousWidget 的类,来重写所有需要改变的功能(比如静态的 needsBatteries 方
法)。
#import <Foundation/Foundation.h>
#import "MyWidget.h"
@interface MyAutonomousWidget : MyWidget
{
}
+ (BOOL)needsBatteries;
@end
#import "MyAutonomousWidget.h"
@implementation MyAutonomousWidget
+ (BOOL)needsBatteries {
return NO;
}