1、ObjectCObjective-C 2.0 程序语言介绍面向对象技术使程序开发和设计更接近自然的方式,也使程序开发更迅速,更容易修改,更容易读懂,大部分面向对象的开发环境都至少由下面三部分组成: 面向对象的程序语言,以及支持的库 一个基础对象框架库 一组开发工具本文档就是关于第一部分-面向对象的程序语言Objective-C的介绍,介绍了程序语言本身以及它的运行环境,为进一步去学习第二部分- Mac OS X Objective-C 应用开发框架库(Cocoa)打下基础。你能通过学习Getting Started with Cocoa.学到更多关于Cocoa 的开发技术。开发Objectiv
2、e-C的两项主要工具是Xcode和Interface Builder,学习Xcode User Guide和Interface Builder可以熟悉这两项开发工具。Objective-C语言是为写出优雅的面向对象程序而设计的简单的编程语言。Objective-C是在ANSI C语言的基础上进行小的但强大的扩展,它在C的基础上扩展了大量基于Smalltalk(第一种面向对象的编程语言之一)的面向对象的部分。Objective-C让C语言拥有全面的、并且通过简单容易的方式就可以实现的面向对象的能力。对于没有面向对象编程经验的程序员,本文也能帮助你成为面向对象编程的能手,它能教会你如何进行面向对象
3、程序的开发。谁适合去阅读本文本文档适合对下面几项内容有兴趣的读者: 学习面向对象的编程 学习基础的Cocoa应用开发框架 Objective-C语言编程本文档既包括基于Objective-C的面向对象模型的介绍,也包括对Objective-C语言本身的介绍。集中介绍了Objective-C中扩展于C的部分,对C语言不做介绍。因为本文不是关于介绍C的文档,它假定读者已经熟悉C语言了,但不要求对C语言非常的精通,Objective-C在面向对象的程序设计上与ANSI C有很大的差别,即使您对C语言不精通,也不妨碍您使用Objective-C进行面向对象的编程。文档结构这个文档分为几个章节和两个附录
4、。下面几个章节描述的是Objective-C语言,它们涵盖了所有扩展于标准C和C+的特性,这些章节虽然只涉及到语言本身,但也必然接触到基础的运行时系统。 对象和类 定义一个类 属性 Protocols Fast Enumeration 消息的工作方式 Enabling Static Behavior 异常处理 线程Apple编译器是基于GNU Compiler Collection的编译器。Objective-C语法支持GNU C/C+语法,Objective-C编译器可以编译C、C+和Objective-C源代码。编译器通过源代码文件名的扩展名为.m识别源文件为Objective-C源文件,
5、源代码文件名的扩展名为.c识别源文件为标准C源文件,源代码文件名的扩展名为.mm识别为在Objective-C中使用C+的源文件。详细的介绍参见章节“在Objective-C中使用C+”。“运行时系统”关注类NSObject,主要介绍NSObject与运行时之间的关系,在特定条件下,它是如何管理对象的内存分配、在运行时如何动态的加载新类、以及对象间的消息传递。附录包括一些对语言有助于理解的参考资料:“语言概要”列出了所有Objective-C扩展于C语言的部分的概要。“语法”列出了Objective-C扩展于C语言的部分的形式语法,它可以和C语言的参考手册一起来使用,C语言的参考手册参见由Br
6、ian W. Kernighan和Dennis M.Ritchie合著,Prentice Hall出版的C语言圣经The C Programming Language。约定文档中涉及到function、method以及其他程序元素时,使用斜体字。如下面语法:interface ClassName (CategoryName)表示interface有两个元素被要求,但也可以选择ClassName和CategoryName。在一些代码中,省略号表示有大量部分被省略了,如下:- (void)encodeWithCoder:(NSCoder *)codersuper encodeWithCoder:c
7、oder;.这些约定也适用于附录中。参见Object-Oriented Programming with Objective-C从一个Objective-C程序员视角讲述了面向对象的程序开发。Objective-C 2.0 Runtime Reference描述了Objective-C运行时库的数据结构和函数,你的程序可以通过这些接口与Objective-C运行时库进行交互。例如:你能增加类或方法,或是获取所有加载类的类定义列表。Garbage Collection Programming Guide介绍了用Objective-C实现的垃圾收集系统。Objective-C Release No
8、tes描述了在最近的Mac OS X系统中Objective-C运行时的改变。对象和类对象从字面上理解,面向对象编程是围绕对象构建的。一个对象包含了数据和一组操作,这组操作可以使用或是修改对象的数据。在Objective-C中,这组操作被称为对象的方法(method),它们能影响的数据是它们的实例变量(instance variables),本质上,一个对象就是一个数据结构(instance variables)和一组程序(method)打包而成的一个自包含程序单元。比如你要写一个绘图程序,这个程序允许用户通过直线、圆、矩形、文本、位图等组件进行绘图,你需要创建许多用户能操作的基本形状类。比方
9、说,矩形对象,有许多的实例变量:标识矩形位置、矩形长和宽,矩形颜色、是否填充,以及用于绘制矩形的线型样式。矩形类也需要许多方法:设置位置、尺寸、颜色、是否填充、线型样式,以及绘制自己。在Objective-C中,对象的实例变量定义在对象的内部,通常,只能通过对象的方法才能访问这些变量(你也可以让子类和其他对象直接访问对象的实力变量,这需要使用范围定义指令,详细请参见“实例变量范围”)。因此其他对象要获得对象的信息,这个对象必须提供了一个这样的方法。例如:一个矩形应该有方法可以获取到它的尺寸和位置。此外,仅有针对对象设计的方法才能被看见,不能错误的将其他类型的方法使用在这个这个对象上,Objec
10、tive-C中的实例变量就像C语言中的局部变量一样,被隐藏起来不为其他程序可见。对象隐藏了它的实例变量和它的方法的实现。id在Objective-C中,对象标识被作为一个特殊的数据类型:id。这个数据类型定义为引用对象的指针。实际上,是指向对象实例变量的指针。像一个C函数和数组一样,任何对象都有一个它自己的地址标识。所有的对象,不管它的实例变量和方法,都统一使用id作为它的标识。对于基于面向对象的Objective-C,像方法返回值类型,id取代int作为缺省数据类型(对于纯的C语言部分,像函数返回值类型,缺省仍然为int)。关键字nil被定义作为空对象,表示id的值为0.id,nil以及其他
11、Objective-C的基本类型被定义在头文件objc/objc.h中。动态类型id类型是完全没有限制的,就自身而言,它不提供关于这个对象的任何信息,除非,它自己是一个对象。但是对象并不都是一样的,一个矩形不可能和一个位图对象有同样的方法和实例变量。在一些情况下,程序需要找出关于这个对象所拥有的更多的信息,如这个对象的实例变量有哪些,它提供了哪些方法等等,即使id类型不能提供这些信息给编译器,每个对象自己也是可以提供这些信息给运行时的。这是可能的,因为每个对象都通过携带一个isa的实例变量标识出对象的类(class)-它描述了对象的类型。每个矩形对象都能够告诉运行时系统它是一个矩形,每个圆都能
12、告诉运行时系统它是一个圆。同类型的对象都拥有相同的行为(method)和相同类型的数据(instance variable),同类型的对象都是这个类型中的一员(member)。Isa指针也能为对象提供反射功能找出他们自己(或其他对象)的信息,编译器为运行时系统记录关于类定义的数据结构,运行时系统函数使用isa,去找出在运行时这些对象的信息。例如,使用运行时系统你能找出这个对象是否有制定的方法,或是找出它的超类的名字。对象的类的更多的细节的讨论请参看章节“类”。通过静态类型给编译器关于一个对象在源代码中使用的类名也是可行的,类被看成一种特殊的对象,类名能被提供通过类型名来提供,具体参见“类类型”
13、和“Enabling Static Behavior”。对象消息这个章节主要介绍发送消息的语法,包括你如何去构造消息表达式。也讨论对象实例变量的“能见度”问题、多态和动态绑定的概念。消息语法让一个对象去执行一些动作,你需要通过它提供的方法向它发送一条消息,在Objective-C中,消息表达式需要使用中括号括起来:receiver messagereceiver是一个对象,message告诉它要去做什么。在源代码中,消息是方法名和需要传递给它的参数。当一条消息被发送时,运行时系统从接收者的方法列表中选取合适的方法引用它来处理消息。例如下面的方法就是告诉myRect对象去执行display方法,
14、让myRect将自己绘制出来。myRect display;方法也能携带参数。下面的例子是发消息设置myRect的位置在窗口上的坐标为(30.0, 50.0):myRect setOrigin:30.0 :50.0;在上面的例子中,setOrigin:是方法名,每个参数前面都有一个冒号,参数跟在冒号后面。这个方法是用了非标签参数,非标签参数将难以描述参数的类型和目的。更一般的是使用带标签的参数,参数标签在冒号之前。如setWidth:height:很清楚的描述了两个参数的意图:myRect setWidth:10.0 height:15.0;方法也可以带可变个数的参数,但是它们比较稀有。可变的
15、参数通过逗号区隔放在放在方法名的后面(与冒号不同,逗号不是方法名的一部分)。在下面的例子中,假定makeGroup有一个固定参数(group)和三个可选参数。receiver makeGroup:group, memberOne, memberTwo, memberThree;跟标准C函数一样,方法也可以返回值。下面的例子假定myRect画一个实心举行返回YES,否则返回NO。BOOL isFilled;isFilled = myRect isFilled;注意:方法和变量可以有相同的名字。消息表达式也支持嵌套。下面的例子是设置一个矩形的颜色为另一个矩形的颜色:myRect setPrimar
16、yColor:otherRect primaryColor;Objective-C 2.0也支持点操作符,点操作符为引用对象提供紧密(compact)、简单的存取方法,典型的应用是在声明的属性特征中在联合中使用。详情参见“属性”和“圆点语法”。发送消息到nil在Objective-C中,发送一条消息到nil(空值)也是有效的的,但是在运行时不会有任何的反应(也不会有异常)。在实际的应用中,这会给Cocoa带来一些好处。发送给nil的消息,其返回值也可能是有效的: 如果方法返回值是:对象、任何指针类型、任何存储小于等于sizeof(void*)的整数标量、float、double、long do
17、uble、long long,发送给nil的消息,都将返回0. 如果方法返回值是在Mac OS X ABI Function Call Guide 中注册定义的struct,发送给nil的消息,返回值为0, 0将填充返回的struct中的每一个字段.返回其他struct数据类型的,将不被0填充。 除了上面说明的外,其他发送给nil的消息,返回值都是未定义的(undefined).下面的代码片段示意发送消息给nil也是允许的。id anObjectMaybeNil = nil;/ this is validif (anObjectMaybeNil methodThatReturnsADouble
18、 = 0.0)/ implementation continues.注意:给nil发送消息的行为效果在Mac OS X v10.5中进行了改变,对于以前版本的行为效果,请参看以前版本的文档。接收者实例变量方法将自动分配接收对象实例变量,不需要声明它们作为方法的参数。举个例子,primaryColor方法没有任何参数,你获取otherRect的填充颜色(primary color)并将它返回,每个方法都假定接收者和它的实例变量已经分配好了,不需要将它们作为参数进行声明。myRect setPrimaryColor:otherRect primaryColor;这项约定简化了Objective-C
19、源代码,它也被支持在面向对象的程序设计中的对象和消息。消息被发送给接收者,就像一封信被邮递到你的家,消息参数带给接收者之外的信息,并不需要将接收者自己带给它自己。方法仅自动分配接收者实例变量,如果它需要的信息存储在另一个对象中,它必须发送消息给这个对象让它暴露变量的内容。primaryColor和isFilled显示了刚才这个意图的用法。参见“定义类”了解更多关于实例变量的信息。多态性基于上面的例子的描述,在Objective-C中消息的看上去和标准C中的函数调用相似,但因为方法属于一个对象,消息的行为与函数调用还是不同。在一些情况下,一些对象仅能对针对它设计的方法起作用,它不能被其他类的方法
20、使用,即使这些类有同样名称的方法。这意味着两个对象对同样的消息有不同的响应。例如,每个类的display消息都通过自己的方式“显示”它自己,圆和矩形对同一个消息有不同的画线轨迹。这个特性,被称为多态性,是面向对象程序设计中的一项重要的特性。和动态绑定配合使用,可以使你写的代码提供给任意数量不同类型的对象使用,这些对象不需要在你写代码时就已定义好,它们可以是你以后再开发的对象,也可以是其他项目的其他对象。例如你可以写代码给id变量发送display消息,任何有display方法的对象都是一个潜在的接收者。动态绑定函数和消息的最根本不同是:函数和它的参数是在编译时就已经确定下来了,但是消息和接收对
21、象直到程序已经在运行、消息在传递时才能确定下来。因此,消息的方法绑定只有在运行时才能确定,并不是在编译时。确切的消息方法实现依赖消息的接收者,不同的接收者可能有有相同的方法名不同的方法实现(多态性)。对于编译器而言,要找到消息对应的确切的方法实现,它就必须知道接收对象的类型它所属的类,但是,接收者的信息(包括它的类型)只有在消息接收时才能确定(动态类型),通过源代码的类型定义并不能完全确认接收者的类型。方法的实现的选择发生在运行时,当消息发送时,运行时消息例程查找消息接收者和消息方法,找到消息方法实现的机器码,呼叫这个方法,传递给它消息实例变量指针。(更多详细的了解,请参见“消息如何工作”)。
22、方法名被用于作为方法实现的选择,因为这个原因,消息的方法名经常被提及为“selectors”。动态绑定和多态性相辅相成,为面向对象程序设计提供了许多灵活性和强大的能力。即使每个对象都有它自己版本的方法,也可以通过一个程序获取多样的结果,这样做并不需要多个程序,只需要多样性的接收者对象即可。在程序运行时才确定接收者,能够根据用户的不同行为了调整在执行的方法实现。当执行基于应用套件的代码时,例如,用户点击菜单上的命令“剪切、拷贝和粘贴”产生的消息,无论当前选择的什么对象接收到消息,都可以根据自己的方式进行响应,显示文字的对象和显示扫描图像的对象对“拷贝”消息的响应是不同的,描绘一组形状的对象和描绘
23、一个矩形的对象响应也是不同的。在运行前,消息都没选择方法(方法未绑定到消息),这些差别是因为这些方法都是独立的响应这些消息的。发送消息的代码不需要关注它们,也不需要对各种可能进行列举,每个应用都可以创造自己的对象,通过它们自己的方式响应copy消息。Objective-C的动态绑定更进一步,它甚至允许发送的消息是可变的,消息在运行时才决定(方法选择器),关于这些更多的讨论参见“消息如何工作”。类面向对象程序构建在多种对象之上的,基于Cocoa框架的程序可能使用NSMatrix对象、NSWindow对象、NSDictionary对象、NSFont对象、NSText对象、以及许多其他的对象。程序之
24、中经常用到同一个类型或类的超过一个的对象比方说几个NSArray对象,几个NSWindow对象。在Objective-C中,通过定义类来定义对象,类定义是一组对象的蓝本,它声明了类的每一个成员为实例变量,定义了这个类所有对象能使用的方法。编译器为每个类创建一个“类对象(class object)”,类对象知道如何去创建一个新的属于这个类的对象(基于这个原因,传统上称类对象为对象工厂(factory object)。类对象是类的编译版本,它构建的对象是类的实例,在你的程序中执行主要工作的是这些由类对象在运行时创建的对象实例。类的所有对象都有相同的方法,它们也拥有从一套模子里拷贝出来的实例变量,每
25、个对象存储它自己的实例变量,但是方法是共享的。一般约定,类名都是首字母大写,实例名都是首字母小写。继承类的定义是累进的,任一个新类都是在另一个类的基础上定义的,新类继承了它的方法和实例变量。新类只是在继承类的基础上进行了增加或修改,它不需要去复制继承的代码。继承使所有的类构成一颗树型结构,仅有一个类作为这棵树的根(root),基于Foundation框架的代码,根类指定是NSObject。每个类(除根类外)都有一个超类(superclass)更接近根类,任何类(包括根类)都可以作为其他类的超类更远离根类。下图(Figure 1-1)描述了绘图程序的几个类的层次关系:这个图显示了Square类是
26、Rectangle类的子类,Rectangle类是Shape类的子类,Shape是Graphic类的子类,Graphic类是NSObject的子类。继承是一个累进的过程。因此,Square对象有Rectangle、Shape、Graphic、和NSObject定义的方法和实例变量,这些方法和实例变量的定义也可以认为是针对Square的。也可以这么说,一个Square对象,不仅是一个Square,也是Rectangle、Shape、Graphic、NSObject。每个类(除了NSObject)都是另外类的专有化和个性化改造,每一个相继的子类都累进了它继承的类更多的修改。Square类定义了由R
27、ectangle转换为Square最小的要求。当你定义类时,你就将它加入到它声明的超类的继承树上,你创建的每一个类都必须是另一个类的子类(除非你打算定义另一个根类)。提供大量丰富的类可以作为超类,Cocoa包括NSObject类和几个包含超过250个附加类的框架,一些类是“现货供应的”-直接应用到你的代码中,另一些需要根据你的需求定义新的子类。一些框架类定义了绝大部分你需要的部分,但留了一部分优雅的实现需要在子类中实现,因此你能通过创造一些优雅的子类写少量的代码,重用框架程序。NSObject类NSObject是一个根类,并没有超类。它定义了基本的Objective-C框架对象和相关信息。它为
28、从它继承的类和类的实例提供了具备对象行为和与运行时系统配合的能力。即使不需要从其他类继承任何特别的行为的类也必须要是NSObject的子类。类的实例在运行时中至少要像Objective-C对象的行为,从NSObject类继承这种能力是最简单最可靠的方式,而不需要你重新去创造新的类定义。注意:提供一个新的根类是一项需要相当的技巧并有巨大风险的工作,这个类需要复制大量NSObject类做的工作,比方像实例的分配、关联它们到他们的类、在运行时中标识它们。因为这个原因,在Cocoa中,你一般使用NSObject作为根类。关于更多的信息,请查看Foundation框架文档中关于NSObject类和NSO
29、bject协议的部分。继承实例变量(Inheriting Instance Variables)当一个类对象创建一个新的实例时,新的对象不仅包括定义它的类的实例变量,还包括它的超类定义的实例变量,以及它超类的超类,这样一直追索到根类,因此isa实例变量是定义在NSObject中,成为每个对象的一部分,isa连接每个对象到它的类。下图显示了Rectangle的一些实例变量的定义的特定实现,并且指出了它们是从哪里来。需要主要的是,这些变量哪些是Rectangle追加在Shape之上的,哪些是Shape追加在Graphic之上的,以此类推。类也可以并不定义新的实例变量,它可以仅定义新的方法,使用它继
30、承得到的实例变量,甚至它根本就不需要任何实例变量。例如,Square类就根本就不需要为自己声明任何实例变量。继承方法对象不仅可以使用定义它的类的方法,而且可以使用定义它的超类的方法,以及超类的超类的方法,以此类推,所有到根类的继承路径上的类的方法都可以使用。例如,Square对象能使用Rectangle、Shape、Graphic和NSObject类定义的方法,使用这些方法就跟类自己定义的一样。在你的程序中你定义的任何新类都可以复用在继承树上的类写的这些代码。这种继承方式是面向对象的程序设计最主要的益处。当你使用Cocoa提供的面向对象的框架时,你的程序只需要在这些框架类的基础上加入少量的客户
31、化代码就可以实现应用所需要的功能。类对象也从他们的的类继承树上进行继承,但它们没有实例变量(只有实例动作),它们只继承方法。方法重载在继承中有一种特殊的情况:当你定义一个新类时,你定义了一个和父类(或祖先类)同名的方法,这个新的方法将覆盖父类(或祖先类)的方法,在新类中,新的方法将被实例化,新类的子类也继承的是这个新方法。这种现象被称为方法重载。例如,Graphic类定义了display方法,Rectangle通过定义它自己版本的display方法覆盖了它。Graphic的方法对所有继承于它的对象都是有效的,但是Rectangle例外,Rectangle对象使用它们自己版本的display方法
32、。尽管重载阻断了原始版本方法的继承,但是在新类中的其他方法仍可以跳过重载的方法找到原始方法,详情请参见章节“消息发送给自己和超类”。重载的方法也能完全正确的包含吸纳原始的方法,新方法只需要对原方法进行修正或修改,而不是完全取代它,继承树上的类定义了同样的方法,但每一个新版本都包含吸纳它覆盖的版本,方法的实现就能更有效的延伸到所有的类上。尽管子类可以重载继承的方法,但是它不能重载继承的实例变量。即使对象会为每个它继承来的实例变量分配内存,你也不能声明一个新的同名的实例变量,如果你一定要这样做,编译器会报错误。抽象类(Abstract Classes)一些类被设计为仅或主要的目的是用于给其他类继承使用,这些类被称为抽象类。抽象类包含
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1