Chinaunix首页 | 论坛 | 博客
  • 博客访问: 36104
  • 博文数量: 9
  • 博客积分: 510
  • 博客等级: 中士
  • 技术积分: 120
  • 用 户 组: 普通用户
  • 注册时间: 2005-11-27 15:25
文章分类

全部博文(9)

文章存档

2011年(1)

2006年(1)

2005年(7)

我的朋友
最近访客

分类:

2005-11-27 16:37:28

顾名思义,Objective-C就是面向对象的C语言。你可以使用Objective-C编程,但不使用任何非C语言的东西。但那样将是对这语言的浪费。Objective-C使用所有的C语言的内容,同时添加了一些使得它更具面向对象特征的语法。本指南假定你已经知道什么是面向对象编程,同时熟悉C(你不必是这方面的专家,但是我不打算在这里教你应该已经知道的东西)。

Objective-C入门

A first look at ObjC(中文版)



原著:Noah Roberts (jik)

翻译:jeff_yecn


顾名思义,Objective-C就是面向对象的C语言。你可以使用Objective-C编程,但不使用任何非C语言的东西。但那样将是对这语言的浪费。Objective-C使用所有的C语言的内容,同时添加了一些使得它更具面向对象特征的语法。本指南假定你已经知道什么是面向对象编程,同时熟悉C(你不必是这方面的专家,但是我不打算在这里教你应该已经知道的东西)。

我同时必须建议你不要仅仅读这一篇文章,因为它远远不是在Objective-CObjC)方面的终极资源。在互联网上你可以找到其它更好、更详细的学习这种语言的资料。在我的网站:members.xoom.com/jik_you上有一些链接,也许你可以从那里开始搜索。

ObjC,和其他面向对象语言一样,是围绕着对象的,所以让我展示一下对象的构成。在ObjC中,对象有两部分。它有一个界面,是对象的外在表现。同时它有一个实现,就是它的内部工作机制。换句话说,就是外界看到的部分和看不到的部分。有时,这样更容易理解。


界面


ObjC中,界面通常有它自己的头文件(通常是.h),但并不是一定要这样。你可以把界面和实现放在一个文件中,或者把几个界面放在一起,但每个界面是不同的,你必须对此很清楚,否则将会感到非常苦恼。我将只使用在头文件中定义界面的办法。

首先,你必须导入这个对象的超类的界面。你可以使用与“#include”相同语法的预处理标识“import”。事实上,两者之间的区别只是#import会进行检查以确保文件仅被包含一次。有些人选择使用#include,这完全符合语法,但ObjC设计是使用#import,你也许愿意考虑这一点而保护你的界面文件。

如果你使用gcc(或egcs等)来作为你的编译器,也许你会愿意使用开关:-Wno-import,否则,gcc会无休止地提示你使用import来代替include

因此,以下是以一个对象为超类的新类的第一行代码,也通常是所有类的开头。

#import /* gcc运行时库的对象 */

现在,如果你计划引用其他的类,你可以导入他们的头文件,更好的办法是使用@class来声明这些标识是类。界面并不需要知道这个类是什么和怎么做的全部信息,所以引用它的头文件并没有作用,让我们使用@class

@class String; //简单吧?

顺便说一下,ObjC同时使用“//”“/* */”来作为注释,这有时会比较方便。

好了,现在我们准备让世界知道想要什么类型的对象,所以让我们声明它。首先我要说明的是我在声明一个新类,在此之前,你应该定义好这个新类的超类。

@interface NewClass : Object //新类在左边,超类在右边。

我并没有漏掉,就是这样写的。

现在我们声明实例变量,不管他们是不是私有的(我碰巧认为私有的东西是不应该被人看见的,实际上也是这样:P)。我在一对{}中间写上,就象我们在C的结构中做的那样。

{

@public

int aValue;

@protected //缺省情况下只能被子类所见

int value2;

@private //不可见

int mrInvisible;

}

做完这些以后,我们要放入method的原型定义。method只能是公共的,虽然如果我愿意的话,我可以演示给你看如果你访问一个私有的method的时候会产生的编译器警告信息。

method既有的(也有些人叫做工厂的),也有实例的。类method只能由类对象访问,实例method显然由实例访问。例如,我声明了一个保存实例的变量,但在我用它之前,我必须使用一个类method来产生一个这个变量指向的实例,这样我才可以调用它的实例method。我这样做了以后,我就不能再访问它的类method了。在我释放对象实例之前,我只能访问它的类method。描述一个method时,你根据它是类method还是实例method响应在前面放上“+”“-”的符号。然后,在“( )”中你填入method返回值的类型,除非你返回的是代表任何对象的通用类型id。在然后,如果你的method有参数的话,你先写一个“:”,跟着是在“( )”中间的参数类型,再接着是变量名。你可以一直写下去,直到以“;”结束。

同时,在第一个参数之后和下一个“:”之前,你还可以放入一些单词,但单词与“:”之间不能有空格。

也许你现在已经糊涂了,让我们看一个类和实例method

+createWith: (int)value; //足够简单

-(void)say: (String *)what to: (String *)name; //第一个参数是what

// tomethod名称的一部分。

在参数表中间使用额外的单词可以使得method念起来更容易理解。就象上面第二个method,念起来象“say 'Hello' to jik”,而不是“say 'Hello', jik”

可变长度参数表的语法和C语言中是一样的:

-(void)doStuff: (id)format,...;

最后,当我们完成全部的定义以后,我们通过下面的语句告诉编译器我们完成了:

@end

这里谈的都是关于对象的。你可以使用typedef或其他在C中可以放入头文件的语法继续定义对象。


名字的小规则


method名由除了+/-,类型和参数以外的所有东西组成。例如,method“-(void)say: (String *)what to: (String *)name”的名字在内部是“say:to:”。如果你使用gdb ObjC补丁,这也是用名字引用method的方法。

同时,你应该记住ObjC通常会被处理为C语言并传递到C编译器。过程中名字通常会发生改变,你应该牢记这一点而不要做可能会迷惑编译器的事.....就好象:

-roll_d: (int)sides;

-roll: (int)count d: (int)sides;

在这个例子中,两个method都会被gcc编译器转化为以下的C函数:

__i_ClassName_roll_d_()

这个事情发生过在我身上,所以相信我,千万小心。如果我是你的话,我不会在method名的中间使用“_”,在开头使用则没有什么问题。


消息


由于你在实现中将经常使用消息,我们必须首先讨论他们是什么以及在语法上怎么表达。一个消息只是对象method的简单调用。在面向对象编程中,由于它就好象给一个对象发送消息告诉它做某些事,因此称作消息。要表达一个消息,首先是对象的名字,跟着是你要它执行的method以及你要传递的参数,这一切用“[ ]”包围起来。

例如:

[object jumpAround];

[object jump: forward];

“object”可以是一个类也可以是类的一个实例。通常类以大写字母开头,而实例则以小写开头。如果“object”是类的话,你只能要求它执行类method,反之亦然。

例如,要建立类“Object”的一个新实例,我们可以使用...

[Object alloc];

“alloc”是为新对象分配内存的类method。但这样并不能使得对象能够使用,因为它的实例变量还没有初始化...要那样做的话,我们调用“init”...因此,我们需要做的全部事情是:

id object=[Object alloc];

[object init];

如果消息的返回值的类型合适的话,你也可以使用它作为另一个消息的一部分。由于“alloc”消息返回id类型,我们可以用这个返回值作为消息发送的对象。由于空返回值不能做任何事情,因此通常返回id会比返回void要好。在你的method执行完成后,最好能够返回指向当前对象自身的变量“self”,除非你不想它作为其他消息的参数或发送对象...如果那样的话,返回void吧。一个使用空返回值的好例子是释放内存的method,不好的例子是分配内存的时候。所以,我们可以使用这个特性来在行中完成分配内存和初始化的工作:

id object=[[Object alloc] init];

当然,你必须确定init method也返回id,否则我们不能继续使用“object”变量。

消息的执行按照先进先出的顺序。如果你把一个消息作为另一个消息的一部分,所有的内层的消息回在外层的消息执行前执行和返回。最终的返回值为最后一个执行的消息的返回值,内层的消息不会在最后返回。


实现


这是对象完成所有工作的部分。它定义所有对象的method的内部工作机制,准确的说,是所有非继承的部分。实现文件的后缀为“.m”

首先,你需要导入类自己的和这个对象中将要用到的类的头文件。象通常一样,你可以做额外的definetypedef以及其他你可能会在C源程序开头做的事情。

当你完成所有的准备工作,你告诉编译器你开始定义一个新对象的实现部分。你用与界面定义类似的method来进行,但你不在需要指明超类是什么,除非你没有为这个对象创建一个界面(这是可能做到的)。

当然,如果你愿意的话,你也可以在说明一次超类是什么,没有任何规定说明你不能那样做。你可以用创建界面定义一样的method来做。

@implementation NewClass

在这里你不需要添加实例变量,所以在实现中不需要包括那一部分。我们现在要做的是定义新的method,以及我们需要覆盖的继承下来的method。有关的语法是一种界面中的 method声明和C函数的交叉。

以下是一个简短的method定义的例子:

-sayHello

{

printf("Hello world! ");

return self;

}

下面一个例子是有参数使用消息的:

-say: (String *)What

{

printf("%s ",[what cString]);

return self;

}

你用与界面定义相同的方法结束实现定义。

@end


特殊的method


当你在派生一个类的时候,你要知道有些method的情况有些特殊。在不同的API中,它们的称呼有所不同,但其中相似的已经分类在一起。有些method在多数情况下都不应该被覆盖,有些可以被覆盖,在你这样这样做的时候必须满足一些特定的要求。

你不应该覆盖“alloc”method。这个method做了些特别的事情来为新的实例分配存储空间。它已经被在Object

阅读(1836) | 评论(0) | 转发(0) |
0

上一篇:什麼是 GNUstep ?

下一篇:Mac 编程浅谈

给主人留下些什么吧!~~