1.2 理解编译过程

在开始详细介绍Objective-C 的语言特性之前,你必须全面理解编译过程。编译过程是计算机将敲入的代码转换成可以真正执行的指令的过程。最终,计算机实际只能执行用它们的“母语”(机器语言)编写的指令。这样的语言通常极其繁冗,人类很难理解和使用。因此,在编码时我们使用的是Objective-C这样的高级语言。我们在文本编辑器中编写代码,保存文件到硬盘,然后针对文本文件运行编译器。编译器接收文本并将其转化成计算机可以执行的指令。

这其中的大部分都是基础知识。你要是已经很熟悉编程(可能用另一种语言),就可能会知道这些,或许可以跳过接下来的几节。但如果你是编程新手,这些知识就很有用了。尽管实际上编译过程是很短的,但理解这个过程还是很有意义的。

理解编译过程的第一步就是编写代码。

1.2.1 编码

前面提到,作为程序员你会花99%的时间将文本打造成软件。在所有的软件开发中,程序员在编译前都需要将计算机指令敲入到一个文本编辑器中。这通常就称为“编码”。

实际上这无非就是敲出指令并将指令保存为文本文件。我们通常将这些文件及其包含的指令称为源代码。

很多编程语言都包含接口和实现的概念。接口通常是一个模块公开给另一个模块的方法和属性。接口实现就是为了兑现对接口中另一个模块的承诺,而让计算机实际执行的指令。换句话说,接口就是系统的一个模块对其他模块承诺它可以干什么,而实现就是兑现承诺所需的计算机指令。

大部分的编程语言根据对接口和实现的处理方式可以分成两种类型。第一类语言不分离接口和实现,它们只使用一个文件在同一个位置声明接口和实现。第二类语言分离接口和实现,使用两个不同的文本文件单独表示接口和实现。Objective-C就属于后者。

Objective-C是面向对象的编程语言。这就意味着开发者需要将程序的不同组件划分成不同的对象和类。类是数据和操作数据的方法的集合,对象就是类的一个单独的实例。Objective-C类包含一个接口和一个实现。

用于存储Objective-C源代码的文本文件名通常有一个.m(实现文件)或者.h(接口文件)扩展名。比如,在创建一个名为MyClass的类时,会为接口和实现分别创建文件名为MyClass.h和 MyClass.m的文本文件,分别用来保存接口和实现对应的计算机指令。代码清单1-1和代码清单1-2显示了上述两个文件。目前,不必急于理解它们的内容。这里列出来就是让大家感觉一下所谓的实现是什么模样。

代码清单1-1 接口文件

@interface MyClass : NSObject
{
    int foo;
}
@property (nonatomic) int foo;
-(void)someMethod;
@end

代码清单1-2 实现文件

#include "MyModule.h"

@implementation MyClass
@synthesize foo;

-(void)someMethod
{
    NSLog(@"some method got called");
}
@end

再次提醒一下,目前不要急于去理解这些代码,但我要借此机会介绍上述代码清单中一些有趣的特征。

首先,在接口文件中,@interface指令清晰地表明这是一个接口。接口以@interface开始并以@end结束。这里可以分为几个部分。

第一部分位于接口头部,通过大括号({})分隔。在这里声明成员变量。成员变量存储和该模块相关的数据。

第二部分用于声明属性和方法。属性记录并控制对对象状态和数据的访问。方法是用于操作数据的计算机指令。上述代码就是一个典型的面向对象模块。第3章将介绍更多关于面向对象编程、类、成员变量、属性和方法的相关知识。

说明

Objective-C保留了C语言的基本特性。因此,在Objective-C中用纯C创建一个模块是不可能的。在这种情况下,模块中的接口文件和实现文件的扩展名分别为.h和.c。此外,如果你在命名实现文件时使用.cc作为扩展名,Xcode会按照C++来编译,使用. mm 作为实现文件的扩展名时,会将其当成Objective-C++ (这就是Objective-C/C++混编)来编译。在本书中,我们将学习Objective-C,因此就不会进行此类操作。

你可以使用任何可以保存纯文本文件的文本编辑器进行Objective-C 编码。不过苹果在Mac OS X上提供了一个卓越的Xcode集成开发环境,适用于Mac OS X、iPhone和iPad应用开发。这真是一个绝好的工具,同时也是本书选用的工具。当然如果在其他平台上进行开发,就需要寻找一个适合所选平台的文本编辑器。

1.2.2 源码、编译代码和可执行文件

在创建源码后,需要计算机将源码编译成它可以执行的指令。这个过程就称作编译源码。

编译源码通常包括下面几个步骤。

第一步称为预编译。可以将预编译想象成计算机为编译代码所进行的准备。在这一步中,编译器会移除一些注释等不会变为可执行程序的代码。它同时也会展开部分代码并重新排列某些指令,以使得编译的第二步更高效。编译第一步的结果就是一个源代码的中间状态。你通常不会看到或处理代码的这种中间状态,但利用编译器选项可以在你想看输出结果时让编译停止在该阶段。一些进行高级开发的程序员有时会使用这些编译选项来查看中间文件和编译器具体的工作过程。在通常的应用开发中可能永远也用不着这样做。

预编译后,第二步就是编译器将源代码真正转变成目标文件。目标文件的扩展名是.o。编译源码可能会花很长时间,因为要编译很多不同的模块和不同的源码文件。在某些模块自身以及依赖的模块都没有改变的情况下,不重新编译这些模块可以大大缩短编译时间。因此,目标文件通常会存储在编译目录中。在上次编译后源码文件没有改变的情况下,编译器就会跳过该源码文件的编译并复用上次编译产生的目标文件。通常,在日常使用中也不需要查看这些文件。它们仅供编译器使用。编译的最后一步称作链接。链接就是将上一步生成的目标文件连接起来以形成可执行程序。除了目标文件外,库和框架也会被链接到可执行程序中。链接过程的结果就是实际的应用可执行文件。在命令行应用中,链接的结果就是一个可以在命令行运行的二进制文件。而在桌面应用中,结果通常是一个应用包,也就是硬盘上的一个包含可执行程序和图片、声音等运行应用所需资源的目录。下一节我们就会介绍应用包。

在Xcode中,单击Bulid和Run按钮就可以编译应用。如果在示例项目中这样操作,就会在控制台启动应用并显示它的输出,这样Xcode就会切换到调试模式,这时你也可以调试应用。调试是一个高级IDE主题。关于如何使用Xcode进行调试可以参考Xcode文档。

编译过程的最终结果就是可执行文件。如果编译器在任意时刻发现源码中的错误(实际上这是很常见的),就会停止处理该文件并显示错误,这样你就可以改正错误,可惜的是编译器相当挑剔。在通过文字推断含义方面,电脑永远逊色于人类。结果就是编译器不会猜测你想敲入什么代码,而是直接放弃并显示错误。这些错误简单到漏写了分号、漏写了空格、不正确的大写以及许多琐事,请准备好——选择了程序员生涯,就要每天面对成百上千个此类错误。即使最优秀的程序员也很少能写出第一次就可以完美编译的代码。

1.2.3 查看应用包

在上一节中,我提到了应用包这个词——即使你是一个有经验的程序员,对你来说这也可能是个陌生的词。你可能想知道这到底是什么。与其说应用包是一种Objective-C结构,不如说它是一种操作系统结构。Objective-C语言本身并不需要或者生成应用包。尽管如此,应用包对于使用Objective-C的几乎所有平台上的编程来说仍是一个重要的、必不可少的概念。这是作为Objective-C程序员需要理解的一个重要概念。

应用包只不过是硬盘上包含一组文件的目录。在Mac OS X中,应用包可以用来集合应用运行所需的所有文件。这包括可执行程序、图片、音频文件和用户界面资源等。此外,在为Mac OS X编译界面应用时,用户界面定义通常是在名为Interface Builder的应用中完成的。该应用也会生成名为NIB文件的包。NIB文件通常有.nib后缀并存储在应用包内部。

说明

NIB的全称是NeXTstep Interface Builder,这是NeXT时代的遗留物。最近,它的格式从二进制变成了XML,相应的文件名后缀也从.nib变成.xib。在编译过程中,XIB文件仍然会被编译成NIB文件,所以在应用包中,你会看到.nib而不是.xib。

代码清单1-3显示了一个典型的应用包目录结构。在本例中,这是Xcode应用包的一部分。

代码清单1-3 应用包目录结构

Xcode.app
`-- Contents
|-- CodeResources -> _CodeSignature/CodeResources
|-- Info.plist
|-- Library
|   |-- QuickLook
|   |   `-- SourceCode.qlgenerator
|   |       `-- Contents
|   |           |-- CodeResources -> _CodeSignature/CodeResources
|   |           |-- Info.plist
|   |           |-- MacOS
|   |           |   `-- SourceCode
|   |           |-- _CodeSignature
|   |           |   `-- CodeResources
|   |           `-- version.plist
|   `-- Spotlight
|       |-- SourceCode.mdimporter
|       |   `-- Contents
|       |       |-- CodeResources -> _CodeSignature/CodeResources
|       |       |-- Info.plist
|       |       |-- MacOS
|       |       |   `-- SourceCode
|       |       |-- _CodeSignature
|       |       |   `-- CodeResources
|       |       `-- version.plist
|       `-- uuid.mdimporter
|           `-- Contents
|               |-- CodeResources -> _CodeSignature/CodeResources
|               |-- Info.plist
|               |-- MacOS
|               |   `-- uuid
|               |-- Resources
|               |   |-- English.lproj
|               |   |   |-- InfoPlist.strings
|               |   |   `-- schema.strings
|               |   |-- Japanese.lproj
|               |   |   |-- InfoPlist.strings
|               |   |   `-- schema.strings
|               |   `-- schema.xml
|               |-- _CodeSignature
|               |   `-- CodeResources
|               `-- version.plist
|-- MacOS
|   `-- Xcode
|-- PkgInfo
|-- PlugIns
|   |-- BuildSettingsPanes.xcplugin
|   |   `-- Contents
|   |       |-- CodeResources -> _CodeSignature/CodeResources
|   |       |-- Info.plist
|   |       |-- MacOS
|   |       |   `-- BuildSettingsPanes
|   |       |-- Resources
|   |       |   |-- Built-in Build Settings Panes.pbsettingspanespec
|-- Resources
|   |-- AskUserForNewFileDialog
|   |-- CreateDiskImage.workflow
|   |   `-- Contents
|   |       `-- document.wflow
|   |-- DevCDVersion.plist
|   |-- Document-Cert.icns
|-- _CodeSignature
|   `-- CodeResources
`-- version.plist

需要注意的是Contents/MacOS目录中包含Xcode可执行程序。该可执行程序和命令行应用生成的可执行程序是一样的。不同的是该可执行程序被打包到了应用包内部并且包含从包加载资源的代码。应用中包含一些有意思的目录,包括PlugIns目录,其中同样也有包。Spotlight目录包含以SourceCode.mdimporter和uuid.mdimporter目录形式存在的包。本书的第二部分将要介绍的Foundation框架包含一些读取应用包的方法,并且该框架支持访问这些内嵌包。

目前你需要知道的重要一点就是:创建Mac OS X、iPhone或者iPad的图形应用时,Xcode会创建一个应用包。今后如果需要在应用中包含分隔开的资源组,你就需要自己创建应用包。Xcode也支持这样的功能,不过目前你还不需要考虑这些。

1.2.4 编译设置

Xcode中有两处可以设定项目的编译设置。第一处也是主要的一处就是项目信息窗口,可以通过选择Project > Edit Project Settings打开该窗口。在本节中,由于这些信息很有用,所以我会详细介绍这些窗口。由于这属于本书讨论的范畴,如果我不介绍你就可能无法理解其中的很多东西。我建议你略过本节并在学完第2章后再回过头来学习。如图1-9所示,项目设置窗口的第一个设置面板是项目的通用设定。其中的大部分都不需要改变,不过你可以利用Configure Roots and SCM按钮配置源码管理方式。

图1-9 项目设置

Place Build Products In 设置用于配置编译后可执行文件在项目中的存放位置。再次指出,这也是一个通常不需要改变的设定,但它在需要确认可执行文件存放位置的时候就很有用。在该目录中可以找到可执行文件。

这之后的设置Place Intermediate Build Files In用于设置编译时源码的目标文件的存放位置。

接下来的Build Independent Targets in Parallel设置会影响编译器为不同的独立平台(比如PPC和Intel)编译目标文件。如果勾选该选项,就会并行编译不同的目标,否则就会逐个编译。

使用Xcode时经常会问的一个问题就是“如何修改自动创建的位于源码文件头部版权声明后的公司名字?”接下来的Organization Name就可用于设置公司名称。在这里设定公司名后,如果向项目添加新文件,版权声明中就会将在此设定的公司名作为版权的所有方。应该将Organization Name设置成公司名或者人名。

接下来的Base SDK for All Configurations用于配置应用默认要编译和链接的SDK。所使用的SDK规定了项目中可用的代码补充和框架。阅读本书以及练习项目时可以将基准SDK设定成当前Mac OS的版本。但是,如果你是在其他平台上进行开发,比如iPhone OS等,就要合理配置该设置。在将应用编译成一个更早版本的Mac OS时也需要修改该设定。

说明

SDK(Software Development Kit,软件开发套件)是库、工具、文档及源码文件的集合,用于在特定的平台上编译应用。Xcode自带了Mac OS X和iOS的SDK。

该面板上的最后一个设置是Rebuild Code Sense Index。你可能会遇到代码感应索引(Code Sense Index)被破坏的极为罕见的情况。在这种情况下,你可以使用该按钮重新创建代码感应索引。这种情况很少见,但如果需要这样做,就可以在这里实现。

项目的编译设置可以在两个地方进行。第一个就是项目级的,第二个就是单个目标文件级的。如果你想将项目级的设置作为项目编译的基准设置,那么目标文件级的设置就是针对特定目标而需要改变的项。比如,一个给定的应用的项目有调试目标和发布目标。在本例中,可以将两个目标的项目级设置设置成一样的,但也可以有一些不同,比如是否需要裁剪可执行文件等。Xcode通过使用两个单独的窗体来包含项目范围的设置和目标范围的设置,从而支持这一操作。如果进行项目级设置,并且没有更改特定目标的设置,那么项目的设置结果也会影响到目标。如果对特定目标的设置进行更改,那么该更改就会重写项目级的设定。图1-10显示了这些设置。

图1-10 目标设置和项目设置

在该示例中,可以同时看到项目级设置和目标级设置。该示例应用配置了两个共享相同代码但编译不同可执行文件的不同目标。可以看到,对于项目级设置和目标级设置不同的项,其项目级设置会用黑体显示。如果选择删除项目级设置,黑体就消失了,取而代之的是从项目级设置获取到的默认值。

屏幕顶部的下拉菜单用来选择所要编辑的配置,比如是调试编译还是发布编译,以及用于过滤特定设置的列表。在项目层面上显示的配置选项是用Configurations选项卡设置的。图1-11显示了一个配置了调试编译和发布编译的常见项目。

图1-11 编译配置

你可以将配置看做相同目标的不同编译版本。除了调试和发布的编译版本之外,另一种常用情形就是你需要针对不同的架构进行编译,比如PowerPC和Intel。

通过xcodebuild命令行工具编译项目也是可行的。这在编译自动化的时候就很好用。在这种情况下,窗口下方的选项Command-line builds use就可以控制在未指定配置的情况下所使用的默认配置。

对于本书的大部分内容来说,大多数设置都不需要改变。也就是说,如果你想了解各个设置的用处,就可以滚动设置页面,单击其中一个并查看屏幕下方显示的描述各个选项的信息。目标编译设置中的第三个标签页是Rules,可以用以配置项目中不同类型文件的默认编译行为。比如,如果某种特定类型的文件在编译过程中需要特殊处理,就可以单击添加按钮添加该文件。然后配置任何你所需要的自定义行为类型。此外,你如果需要改变任何一种内置文件的默认行为,也可以通过下拉列表选项来改变。

大多数情况下,你不需要对设置进行任何改变。在编译带图形界面的Cocoa应用时,在目标编译设置中一个额外的选项卡就是Properties。该选项卡可用于配置项目的可执行文件名以及主包标识符等。通常,你会把标识符配置成和你的公司名相匹配,而不是使用默认值。此外,如果想要通过双击打开应用所保存的文件,可以为这些文件注册并配置一个文档类型。在编译应用时,Mac OS X会检测到你所指定的和应用相关联的文件类型,并且会列出你的应用,表示可以打开这类文件。图1-12就显示了这种行为。

图1-12 应用包属性

说明

对于命令行应用,就看不到Properties选项卡。Properties选项卡实际上配置的是应用包的信息,而这在命令行应用中是不存在的。

如果是图形应用,还可以设置应用图标。可以通过在应用包中包含一个图标文件并在Icon File 设置中指定文件名实现。

对于本书中的大部分开发,你不需要改变其中的任何一个选项,但是知道这些对以后很重要。

说明

本书印刷时还在开发中的Xcode 4对编译设置位置改变了一些,但设置本身几乎是一样的。关于该功能的最新信息请参考Xcode 4文档。

目录