Objective-C 编程/语法
Objective-C 是一种面向对象编程语言,它是在 C 编程语言 之上的一个层级。这意味着如果你知道如何编写 C 代码,只需要学习一些语法上的变化。
在本节中,我们将探讨如何在 Objective-C 中实现类并实例化对象。如果你不熟悉面向对象编程,请参考 Objective-C 概念.
如果你已经学习过 C 语言,你可以跳过本节并继续下一节,关于运行时的说明。如果你没有学习过,或者有点生疏,请继续阅读。
在 C 语言中,代码包含在一个函数中。函数由多个语句组成,每个语句以分号结尾。例如,C 语言中一个简单的加法函数可能如下所示
int add (int a, int b)
{
int c;
c = a + b;
return c;
}
这个示例包含以下几行
- 函数定义(返回一个 int(整数),名为 add,并接受两个参数,都是整数)。// 整数是像 6、66 或 567 这样的整数,而不是像 5.6 这样的带有小数位的数字。
- 函数的大括号开始
- 变量声明
- 赋值语句
- return 语句,将 c 的值返回给调用函数/
- 函数的大括号结束
为了控制流,C 和 Objective-C 使用
- for (<initial>;<condition>;<increment>)
- while (<condition>)
- do ... while (<condition>)
- switch (<condition>)
- if (<condition>) then {<statements>} [else <statements>]
for、while 和 do 语句将继续执行循环,直到条件为假。switch 和 if 语句根据条件跳转到不同的语句。
Objective-C 默认情况下不会实现或导入任何函数。相反,它们需要通过使用 #import
预处理指令来导入。(#include 也可以工作,但通常在 Objective-C 中不使用它 - #import 工作得更好,因为它不会像 #include 那样尝试两次导入相同的东西)
Objective-C 需要一个称为 运行时系统 的东西来提供 Objective-C 特性。运行时负责对象的创建、管理和销毁。
如果你有gcc编译器,你应该已经安装了 Objective-C 运行时。否则,你可能需要单独安装运行时。请注意,gcc 是一个编译器集合;完整的安装不仅包含 C 编译器,还包含 C++、Java、Objective-C 甚至 Fortran-95 和 Ada 2005 编译器。但是,可以只部分安装 gcc,例如只安装 C 编译器。因此,Objective-C 运行时可能已安装在 gcc 中,也可能未安装。请检查你的发行版。
两个主要系统用于运行 Objective-C 程序
- GNU 运行时,它作为 gcc 的一部分在 Unix 和类 Unix 操作系统上提供;
- NeXT 运行时,它在 NeXTSTEP、OPENSTEP 和 Mac OS X 操作系统上提供。
本文档假设你使用的是 GNU 运行时,但这两个运行时系统几乎完全相同。
本文档没有涵盖 OPENSTEP、Cocoa 或 GNUstep 框架,但这里学习到的技能在使用这些系统开发时会很有帮助。
编写 Objective-C 类需要在开始编写任何代码之前做出一些设计决策。假设我们正在编写一个类来表示一个名为Point的点,它位于二维平面上。
我们需要问自己两个问题
- 我们需要存储和使用哪些数据?这与我们需要声明的 实例变量 相关。
- 我们需要执行哪些操作?这与我们需要定义的 方法 相关。
对于这个示例,我们将使用double变量作为 x 和 y 坐标。我们将定义一个方法来获取两个坐标,并将定义一个方法来获取它们与原点的距离。
#import <objc/Object.h>
@interface Point : Object
{
@private
double x;
double y;
}
- (id) x: (double) x_value;
- (double) x;
- (id) y: (double) y_value;
- (double) y;
- (double) magnitude;
@end
让我们检查一下这个接口的每个元素的含义。
#import <objc/Object.h>
@interface Point : Object
该@interface行表示我们开始声明Point接口。我们从另一个名为Object的类继承而来。Objective-C 为你提供了一个通用类,称为Object。该Object类是一个 根类 ——它不从另一个类继承。该Object类提供了一组方法,这些方法为对象提供关键功能,以便 Objective-C 运行时可以识别和使用它们。与 C 语言一样,我们需要包含Object的标题文件,Object.h,在我们使用标题文件中声明的方法之前。
如果你没有显式地从某个类继承,你的类将成为一个根类。(在 Objective-C 中,可以有多个根类。)这可能不是你想要做的,因为创建根类是一个非常棘手且高级的主题,只有在非常特殊的情况下才有用。在大多数情况下,你将想要从Object或从Object继承的某个类继承,等等。如果你在 NeXTStep / GNUstep / Cocoa 中开发,你将主要使用另一个名为NSObject的根类,它提供了与Object.
不同的基本方法。该词import意味着我们只包含该文件 一次。这解决了递归包含问题。
{
@private
double x;
double y;
}
接口声明中的大括号之间的任何内容都指定了类拥有的实例变量。
该@private行是一个可见性修饰符:它表示它后面的实例变量是 私有 的,即它们只能被声明它们的类访问。将所有实例变量标记为私有是一种很好的做法:它们包含对象的狀態,它们不应被更改,除非由对象本身更改。
- (id) x: (double) x_value;
方法的声明位于实例变量之后。这是一个设置 x 值的方法的声明。在 Objective-C 中,通常使用与设置的变量相同的名称命名 setter 方法。
连字符指定了一个实例方法(我们将在后面讨论它们)。然后是方法的名称(此方法称为x- 注意冒号),冒号表示一个名为x_value的参数,其类型为double.
对x_value参数的强制转换是必要的,因为与 C 语言不同,默认类型是id,而不是int。类型id非常特殊——它是一种可以保存 任何 对象的类型。第一个强制转换严格来说不是必需的,但应用于清晰起见。第一个强制转换告诉我们,该方法x返回一个对象。
- (double) x;
这是另一个名为x的方法(没有冒号),但它返回一个double。这是获取 x 变量值的规范。它与之前的方法没有冲突,因为类型不同,并且之前的方法接受一个参数,而此方法不接受任何参数。
@end
在指定所有方法和实例变量之后,此符号标记声明的结束。
接口规范位于.h文件中——一个头文件。通常按照类名来命名文件,因此我们将创建一个名为Point.h.
的文件。
实现#import "Point.h"
#import <math.h>
@implementation Point
- (id) x: (double) x_value
{
x = x_value;
return self;
}
- (double) x
{
return x;
}
- (id) y: (double) y_value
{
y = y_value;
return self;
}
- (double) y
{
return y;
}
- (double) magnitude
{
return sqrt(x*x+y*y);
}
@end
#import "Point.h"
类的实现。我们在上面定义的接口中实现方法。让我们依次查看实现的每个元素。Point同样,我们导入
@implementation Point
的接口,就像我们在 C 语言中一样。
- (id) x: (double) x_value
{
x = x_value;
return self;
}
这是一个标记,它标识实现的开始。x这是一个典型的方法实现。我们可以直接使用x_value变量,而无需声明它,因为它已经在接口中声明,并且只能被该类的 methods 访问。总的来说,它的行为类似于普通的 C 函数。在这里,我们将参数x.
的值分配给实例变量然后,该函数返回修改后的整个当前对象。关键字self
- (double) x
{
return x;
}
代表当前对象。x这是获取
变量值的简单方法。我们只需返回它即可。其他方法的行为应该与上述方法类似。该@end
关键字结束实现。实现规范位于.m文件中。通常按照类名来命名文件,因此我们将创建一个名为.
Point.m
的文件。使用对象[编辑 | 编辑源代码]这是使用我们刚刚创建的类的函数。这是一个典型的 main 函数。
#import "Point.h"
#import <math.h>
#import <stdio.h>
int main(void)
{
Point *point = [Point new];
[point x:10.0];
[point y:12.0];
printf("The distance from the point (%g, %g) to the origin is %g.\n",
[point x], [point y], [point magnitude]);
return 0;
}
让我们来检查一下这里发生了什么。
#import "Point.h"
#import <stdio.h>
我们导入接口到Point以便我们可以使用它。我们导入stdio.h以便我们可以使用printf.
Point *point = [Point new];
这是一个典型的 Objective-C 方法调用
- Point *point声明,从技术上讲,指向类型为Point的实例的指针。实际上,最好将其视为一个持有Point对象的变量,但请记住指针的概念。
- [Point new]调用一个名为new的方法。空格之前的第一个部分表示我们调用方法的对象。第二个部分是方法名称和参数。该new方法称为类方法,因为它不针对类的实例执行操作,而是针对类本身执行操作。该new方法分配内存并初始化对象,使其可以使用。无需显式删除对象,因为 Objective-C 会跟踪对给定对象的引用次数,并在必要时将其释放。
- 注意:该new方法是alloc和init的简写,可能并不总是可用。如果不可用,则可以使用以下方式初始化对象并使其可以使用[[Point alloc] init]
[point x:10.0];
[point y:12.0];
这些是一些典型的实例方法调用。我们调用point的方法x和y。在 Objective-C 术语中,我们说我们发送point一条消息以应用于该方法x.
这些消息分配point的x和y变量。回想一下,该x和y方法包含return self;。这意味着我们可以将这两个消息链接在一起,如下所示
[[point x:10.0] y:12.0];
消息[point x:10.0]返回一个对象,point,其x变量已设置。然后在该对象上,外部消息将其y变量分配给它。
printf("The distance from the point (%g, %g) to the origin is %g.\n",
[point x], [point y], [point magnitude]);
该printf语句包含方法调用[point x],它返回x变量的值以供打印,[point y],它对y变量执行相同的操作以供打印,以及[point magnitude]它计算距离并返回该值。
Objective-C 编程语言:Objective-C 概念 - Objective-C 语法 - Objective-C 深入 - Objective-C 高级功能