Xcode基础:详解Objective-C开发带有图形界面的程序

来源:互联网 发布:苹果电脑编程软件 编辑:程序博客网 时间:2024/06/06 17:50

Xcode基础:详解Objective-C开发带有图形界面的程序

随着知识的积累,我们下面将开始讨论如何编写带有图形界面的程序。我要承认,Objective-C语言是C语言的变种。之前我们讨论的很多东西都是纯粹的C语言的内容。那么Objective-C语言和纯粹的C语言有什么不同?区别就在“Objective”上。Objective-C语言把抽象的概念当作对象。

直到现在,我们主要在处理数字。如你所知,Objective-C语言自然支持数字的概念。也就是它允许你在内存中创建数,并通过数学公式和数学函数操纵这些数。如果你的程序用作处理数字,比如计算器程序,那这就很很有用了。但是如果你的程序是一个音乐播放器,用来处理歌曲、播放列表、艺术家名单等等,那该怎么办?亦或者你的程序是一套航空管制系统,要与飞机、航班、机场打交道,又该怎么办?是不是在Objective-C中编写这样的程序如同编写处理数字的程序一样容易呢?上面这些元素(歌曲、艺术家、飞机、机场等等)都是所谓的“对象”。通过Objective-C语言,你可以定义各种你需要处理的对象并为它们编写程序。

通过一个例子看看Objective-C语言编写的程序是如何处理窗口的,比如Safari浏览器的窗口。Mac微机上一个打开的Safari窗口左上角都有三个按钮。红色的是关闭按钮。如果点击这个按钮来关闭一个窗口会发生什么?一条信息会被发到窗口,窗口接着运行一段使自己关闭的代码来回应这段信息。

窗口是一个对象,你可以随意的拖放它。三个按钮也是对象,你可以点击它们。这些对象都有一个在屏幕上可以看见的代表物,但并不是所有的对象都有可以被看见的代表物。例如存在于Safari浏览器和Web服务器之间的连接,就是一个不可见的对象。

你可以随你的意愿打开任意多的Safari窗口。那么你觉得苹果的开发人员是a.为每一个窗口编写代码,绞尽脑汁预测你最多打开多少个窗口;还是b.创建一个模板使Safari可以随时创建窗口对象?

很显然答案是b。他们编写一些代码,被称作“类”(class),这些代码定义一个窗口的外观和行为。当你新建一个窗口,实际上是这个“类”为你创建了一个窗口。这个“类”就代表了窗口这个概念,或者说每一个窗口都是这个“类”的一个实例。(就像说76是数这个概念的一个实例。)

你创建的窗口都在屏幕上有自己特定的位置。如果你把窗口最小化到Dock上又把它还原,它会回到它在屏幕上原来的位置。这是如何实现的?类定义了专门的变量来记录窗口在屏幕上的位置。类的实例,也就是对象,包含有这些变量的值。每个窗口有自己的值,不同的窗口也就有不同的值赋给变量。

类不仅仅创建窗口对象,而且还给它提供一系列动作。其中一个动作就是关闭。当你点击窗口上“close”按钮,按钮就向窗口发送一条“关闭”信息。能够被对象执行的动作叫做方法(methods)。它们和函数很相似,所以如果你一直跟着我们学到现在,学习它就不会有太多障碍。

当你类创建一个窗口,它同时在内存(RAM)中留出空间来存放窗口的位置信息和其他信息。但是并不包含关闭窗口这个程序代码。那样会浪费计算机的内存空间,因为所有的窗口都是用同样的程序代码执行关闭命令。用来关闭程序的代码只被需要一次,但是每个窗口对象都有通向类中相关代码的途径。

像前面一样,本章下面要看到的例子依然包含着一些涉及内存预留和释放的语句。但是我们仍然要留到后面才讨论。抱歉!

下面要写的这段程序有两个按钮和一个文本区域。当按下第一个按钮,一个值会显示在文本区域;按下第二个按钮另外一个值被显示在文本区域。你可以把它想像成只有两个按钮的计算器,当然它并不能计算。后面你学的多了就可以编写一个真正的计算器了,我只是举一个简单的例子。

程序上的任何一个按钮被按下,都会传送一条信息。这条信息包含要被执行的方法的名称。那么这样一条信息将被发送到什么地方?在关闭窗口的例子中,关闭窗口这条信息被发送给了作为窗口类的实例的窗口对象。这里我们同样需要一个对象来接收来自两个按钮的信息,并且还能告诉文本区域显示哪个值。

所以,我们要先创建一个我们自己定义的类,并且创建一个作为它实例的对象。这个对象会作为来自按钮的信息的接收方。(参考下面的草图。)像窗口对象一样,我们的类的实例也是一个对象,但相比之下我们的类的实例在程序运行时并不能像窗口对象那样在屏幕上显示出来。它只是Mac微机内存中的东西。

当我们的类的实例收到来自按钮的信息,相对应的方法就会被执行。方法的代码是包含在类中的而不是包含在类的实例中。运行时,这个方法发送一条信息给文本区域对象。除了文本区域对象的名称,信息还包含文本区域类的一个方法的名称。接收到这条消息,文本区域对象就会执行方法。我们需要文本区域对象在我们单击按钮时现实一个值。所以这条消息除了包含对象名称、方法名称,还会包含一个方法会使用到的参数(值)。

下面给出的是Objective-C语言信息传递的一般格式,[1.1]不带参数,[1.2]是带参数的形式。

例[1]

1
2
[receiver message];
[receiver message:argument];
如你所见,所有的工作部分都放在中括号里面,而且要在结尾加上那个永恒不变的分号。在括号里,接收对象的名称放在首位,紧随其后的是方法。如果方法要求跟有参数,那么要使用[1.2]那样的形式。

具体看看上面讲的东西是如何实现的。启动Xcode并创建一个新的工程。在“Application”标题下选择“Cocoa Application”。给你的工程取个名字,按照惯例,图形界面程序的名字一般以大写字母开头。在“Groups & Files”一栏打开“Resources”文件夹。双击“MainMenu.nib”一项。

另一个名为界面创建器(Interface Builder)的程序就会启动。好多窗口同时出现,你可能都想从“文件”菜单选择“隐藏其它窗口”了。屏幕上应当显示出三个窗口。一个叫做“Window”,你
的程序的使用者会看到这个窗口。它有点儿大,你可以调整它。“Window”窗口的右边是一个以“Cocoa-”字样开头的窗口。它被称作“调板”,里面存放着可以用于你的图像界面的各种对象。单击调板工具栏上第二个图标,并且拖拽两个按钮到你的图形界面窗口“Window”上。单击调板工具栏上第三个图标,把标有“系统字体文本(System Font Text)”字样的文本区域拖拽到图形界面窗口。

在后台,从调板上拖拽一个按钮实际上是创建了一个新的按钮对象并把这个对象放到了你的程序窗口中。从调板拖拽文本区域或者其它对象情况也是一样的。

请注意,当你把光标停留在调板中的图标上,图标的名字就会被显示出来,比如“NSButton”或者“NSTextView”等等。这都是苹果提供的类的名称。本章的后面我们会学习在我们自己编写的程序需要执行某一动作时如何从这些类里找到相关的方法。

把“Window”窗口中你刚刚拖拽过来的对象排列美观,并把它们的尺寸调整适当。双击按钮对象可以更改它们的名字,但是要一次一个。我建议你在我们写完这个程序之后继续探索一下调板,掌握其它对象的添加方法。

选择一个对象,按组合键command-shift-i就可以修改它的属性。这个也要好好探究一下。比如选择了“Window”窗口(你可以在左下角的窗口的Instances标签页里看到它已经被选中),按组合键command-shift-i。如果窗口顶部的弹出式菜单上是“Attributes”,你就可以选择勾选“TexturedWindow”一项,这个选项使窗口呈现金属外观。也就是说,不用写一行代码就可以在很多方面定义你的应用程序的外观。

下面要编写一个类,为此我们还要深入的研究一下类的工作原理。

为了节省编程耗费的精力,最好就是能够在已有的东西上创建你的新东西,而不是从草稿开始着手。举个例子,你要写一个具有特殊属性(功能)的窗口,只要添加那些定义特殊属性的代码就足够了。而不用再去写那些定义其它内容的代码,比如最小化或者关闭窗口。在其他程序员的基础上工作,你可免费继承他们已经写好的东西。这也是Objective-C语言与C语言的不同。

具体的说,这里有一个窗口类(NSWindow),你就能写一个继承它功能的类。假如你添加了某些行为到你自己写的窗口类中。当你自己编写的窗口接收到一条关闭信息时会发生什么?你没有写也没有拷贝任何代码到你的类中。很简单,你编写的窗口的类如果没有包含常规行为的代码时,信息会自动地上溯到它所继承功能的原始的类那里(称作“父类superclass”)。如果有必要会一直追溯到方法被找到(或者追溯到最为原始的那个类为止)。

如果方法没有被找到,那就意味着你发送了一条无法被处理的信息。这好比你到汽车修理厂修理雪橇,他们却爱莫能助。这个时候,Objective-C语言会报错。

如何去执行你自己定义的行为而不是从父类那里继承来的方法?很容易你就可以跳过个别方法。例如你可以写一段这样的代码:点击关闭按钮只是把窗口隐藏而不真正关闭它。你自己编写的方法要使用和苹果定义的关闭窗口方法一样的方法名称。这样你自己写的窗口收到关闭信息时就会执行你定义的方法,而不是苹果定义的那个。所以这个窗口只会移出你的视线而不是真正关闭。

嘿!真正关闭的方法苹果已经写好了。除了我们写的自己的关闭方法,我们还可以援引父类中提供的关闭方法,这就需要另一个不同的调用语句来确保我们写的关闭方法没有被先使用。

例[2]

1
2
// Code to move the window out of sight here.
[super close]; // Use the close method of the superclass.
这些内容对于这本入门级小册子来讲都是些进阶内容,我们可不指望你凭此寥寥数语就学会。

所有类里面,顶级的是被称作“对象类”(NSObject)的类。几乎所有的你创建或使用的类都直接或间接的是对象类的“子类”(subclass)。比如类NSWindow是类NSResponder的子类,类
NSResponder又是类NSObject的子类。类NSObject定义了适用所有对象的一般方法。(比如产生一段对对象的文本描述,询问对象是否能理解所给信息等等。)

在这些长篇大论的理论是你厌烦之前,看看如何创建一个类。

回到“MainMeni.nib”窗口,选择“Classes”标签页。在第一栏中你会看到“NSObject”。选择它,并在“Classes”菜单中选择“Subclass NSObject”。回到“MainMeni.nib”窗口,给你创建的类取个名字。我叫它“MAFoo”。

“MAFoo”这个名字中前面两个大写字母是My Application的缩写。你可以随意给你的类起名字。一旦你开始写程序了,我们建议你使用相似的方法取名(也就是说选择两到三个大写字母作为所有你编写的类的名称的固定的前缀,以免与现有的类发生冲突)。但是不要使用“NS”做前缀,以免引起混淆。NS是苹果自有的类专用的,它代表NeXTStep。Mac OSX就是在NeXTStep操作系统的基础上发展起来的,苹果公司收购了NeXT公司,此举使乔布斯(Steve Jobs)重返苹果并重坐第一把交椅。

在CocoaDev wiki网站上提供了哪些应当避免作为前缀出现的词汇列表。你可以在编程前访问http://www.cocoadev.com/index.pl?ChooseYourPrefix进行查询。

当你创建一个类的时候,你应当给它取一个能够传达有效信息的名称。例如在Cocoa中,描绘窗口的类叫做NSWindow。另一个例子是描绘颜色的类叫做NSColor。在我们的例子中,类MAFoo是用来说明对象在应用程序中是怎样互相通讯的。所以我们给他取了一个没什么特殊含义的名字。

回到界面编辑器中在Classes菜单选择“InstantiateMAFoo”一项。就像你现在在“Instances”标签页中看到的一样,你有了一个新的叫做MAFoo的图标。这个图标代表了我们刚刚创建的类的实例。

我们下面要作的就是把发送信息的的按钮和我们创建的对象MAFoo关联起来。此外,我们还要创建一个对象MAFoo和文本区域之间的连接,因为还有一条信息回被传送给文本区域对象。如果没有索引,一个对象无法发送信息给其它对象。在按钮和对象MAFoo之间创建关联就是为按钮提供一个通向对象MAFoo的索引。使用这个索引按钮就可以向对象MAFoo发送信息。同样的,建立对象MAFoo与文本区域的关联可以让前者给后者发送信息。

再从新走一遍程序的工作流程。按钮被单击时发送一条定义具体动作的信息。这个信息包含将被执行的类MAFoo中的方法的名称。信息被送到我们创建的类MAFoo的实例——对象MAFoo那里。对象MAFoo本身并不包含执行动作的代码,但类MAFoo中有。所以发送到对象MAFoo的信息转向请求类MAFoo中的方法去做事情:在这个例子中是向文本区域发送内容。像所有的信息一样,这条信息包含着方法的名称。本例中文本区域对象的方法的任务是显示一个值。这个值被作为信息内容的一部分和方法的名称一起被文本区域援引。

我们的类需要两个动作(方法),这两个方法会被两个按钮分别调用。类还需要一个出口,通过一个变量来记录信息被发向哪个对象。

在“Mainfile.nib”窗口的“Classes”标签页中选中MAFoo。在键盘上按下组合键command-shift-i来调出检视窗口。在检视窗口中选择“Action”标签页并点击“Add”按钮来给类MAFoo添加一个动作(也就是一个方法)。重新命名由界面编辑器默认的名称使名字富有含义。(比如可以叫做“setTo5:”,因为我们会通过这个方法在文本区域显示数字5。)添加其它方法并命名。(比如叫“reset:”,我们会用这个方法在文本区域中显示数字0。)注意方法的名称后面以冒号“:”结尾,后面我们再详细讨论。

现在在检视窗口中选择“Outlet”标签页,添加出口并命名(比如叫“textField”)。

在对象之间建立起关联之前,我们还要给两个按钮取名。第一个按钮要在文本区域显示数字5,所以叫“Set to 5”(方法是双击并按钮并键入新名字)。同样,第二个按钮改叫“Reset”。注意,上面给按钮改名这些步骤并不是程序正确运行的要求,而只是想让我们的用户界面看起来尽可能像是面对最终用户的。

现在我们已经可以创建下面这些关联了:
a)按钮“Reset”和MAFoo的实例之间的关联;
b)按钮“Set to 5”和MAFoo的实例之间的关联;
c)MAFoo的实例和文本区域之间的关联。

在“MainFile.nib”窗口中单击“Instances”标签页。按住键盘上的Ctrl键并用鼠标将“Reset”按钮拖拽到MAFoo的实例上。(千万不要使用别的方法创建关联!)一条代表关联关系的线会显示
在屏幕上,确认这条线是从按钮连到了MAFoo的实例上就可以松开鼠标了。

当松开鼠标,检视窗口会显示关联调板,调板中列出了对象MAFoo中可用的方法。选择正确的方法并单击“Connect”按钮来完成关联过程。

现在按钮已经有了通到对象MAFoo的索引,任何时候被单击都会发送包含“reset:”这一方法名称的信息。

用同样的方法创建“Set to 5”按钮和对象MAFoo的关联。

创建对象MAFoo和文本区域之间的关联时要按住Ctrl键并将对象MAFoo拖拽到文本区域上,再单击“Connect”按钮。

上面你做的就是没有写一行代码就创建了关联。

在“MainMenu.nib”窗口选中MAFoo的实例,切换到“Classes”标签页,你会看到在列表中类MAFoo被选中。在“Classes”菜单中选择“Create Files for MAFoo”。界面编辑器会接着询问生成
的文件的保存路径。默认情况下,这些文件会保存到我们保存程序工程的文件夹中,这也正是我们需要的。

现在返回到Xcode中,你可以在工程窗口中看到你创建的文件,这些文件放在“Other Sources”组中。如果需要,你可以把他们拖拽到“Classes”组中,因为你创建的文件本身就代表类。

现在我们要回顾一下第四章的内容,我们在那一章讨论的是函数。你还记得例[11.1]讲到的函数使用时,你必须在使用函数前告诉编译器引入一些东西。在我们刚刚生成的两个文件中有一个叫“MAFoo.h”,它是一个头文件:包含关于我们的类的信息。比如,下面第[3.5]行有NSObject字样,表明我们的的类继承自类NSObject。

例[3]

1
2
3
4
5
6
7
8
9
/* MAFoo */
#import <Cocoa/Cocoa.h> // [3.3]
@interface MAFoo : NSObject // [3.5]
{
IBOutlet id textField; // [3.7]
}
- (IBAction)reset:(id)sender; // [3.9]
- (IBAction)setTo5:(id)sender; // [3.10]
@end
在第[3.7]行,这里有一个去往文本区域的出口。“id”指示对象。“IB”代表界你用来创建代码的面编辑器。

第[3.9,3.10]行中“IBAction”在这里是无效的。没有信息会返回给作为信息发送方的对象:我们程序中的这些按钮不会从对象MAFoo那里得到回应信息。

你还会看到这里有两个界面编辑器动作(Interface Builder Actions)。

前面我们见过和第[3.3]行相似的内容:#import <Foundation/Foundation.h>。尖括号中的内容前者是用于非图形界面程序的,后者用于图形界面程序。

让我们看看第二个文件:MAFoo.m。又是一些免费代码。

例[4]

1
2
3
4
5
6
7
8
9
#import "MAFoo.h"
@implementation MAFoo
- (IBAction)reset:(id)sender // [4.5]
{
}
- (IBAction)setTo5:(id)sender
{
}
@end
首先,头文件MAFoo.h被输入,所以编译器知道要做什么。两个方法“reset:”和“setTo5:”将可以被识别。它们是我们的类的方法。它们和函数类似,所以你需要把代码写在大括号中。在我们的程序中,当你按下一个按钮,按钮会发送一条信息到对象MAFoo,要求执行一个方法。但我们并没有写相关的代码,我们做的仅仅是在界面编辑器中把按钮和对象MAFoo连接起来了。尽管如此,我们必须写出从对象MAFoo发送到文本区域对象的信息的代码。我们在第[5.7,5.12]行给出了这些代码。

例[5]

1
2
3
4
5
6
7
8
9
10
11
#import "MAFoo.h"
@implementation MAFoo
- (IBAction)reset:(id)sender
{
[textField setIntValue:0]; // [5.7]
}
- (IBAction)setTo5:(id)sender
{
[textField setIntValue:5]; // [5.12]
}
@end

如你所见,我们发送了一条消息以出口名称“textField”为参数的信息。因为我们已经在界面编辑器中关联了这个出口到真正的文本区域,所以信息会被发送到正确的对象。这条信息包含方法的名字“setIntValue:”,同时还有一个整数值作为参数。方法setIntValue:能在文本区域对象上显示一个整数值。下面一章我们会讨论如何创建这样的方法。

你现在已经可以编译程序并启动它了。按下Xcode工具条上的“Build and Go”按钮。Xcode可能需要几秒中时间来创建并且运行你编写的程序。最后,程序会出现在屏幕上,你可以试试了。

简单的说,我们创建了一个(很基本的)程序,你仅仅为它写了两行代码。

原创粉丝点击