Objective-C 2.0编程语言

上传人:ning****hua 文档编号:76939492 上传时间:2022-04-19 格式:DOC 页数:47 大小:207.50KB
收藏 版权申诉 举报 下载
Objective-C 2.0编程语言_第1页
第1页 / 共47页
Objective-C 2.0编程语言_第2页
第2页 / 共47页
Objective-C 2.0编程语言_第3页
第3页 / 共47页
资源描述:

《Objective-C 2.0编程语言》由会员分享,可在线阅读,更多相关《Objective-C 2.0编程语言(47页珍藏版)》请在装配图网上搜索。

1、Objective-C 2.0编程语言简介采用面向对象编程语言编程可以让程序在设计上更加直观,开发更加快捷,修改起来更加方便,更容易理解。大部分面向对象的开发环境至少会包含以下三个要素:一个支持库的面向对象的编程语言一个内容丰富对象库一系列的开发工具本文档是关于这种面向对象开发环境的第一要素的,即编程语言及其运行时环境。此文全面介绍Objective-C编程语言,这也是学习第二要素的基础,以及学习基于Mac操作系统,使用Objective-C编写的应用程序框架的基础。这种应用程序框架以Cocoa而闻名遐迩,开发Mac和iPhone应用程序的两个主要工具是Xcode和Interface Buil

2、der。Objective-C是一种简单的计算机语言,但对复杂的面向对象编程方法却能自如应对,它实际上是对ANSI C标准语言的一个小小的扩充,但这种扩充却使它变得相当强大。它对C语言的扩充都是基于Smalltalk的最古老的面向对象编程语言之一。Objective-C的设计目的是给C语言一个通过一种简单且直观的方式而实现面向对象编程的能力。对于那些从来没有使用面向对象语言写过程序的家伙,此文也能帮助他们尝试并开始熟悉一个全新的领域,为他们揭开面向对象的神秘面纱,并给他们带来编写面向对象程序的神秘体验。此文档适合于哪些人此文档适合于那些对以下几点感兴趣的读者:学习面向对象的编程语言为Cocoa

3、应用程序框架打基础编写Objective-C程序此文档主要介绍面向对象的编程模型,这也是Objective-C的根基,它集中介绍Objective-C对C语言的扩展,而不是C语言本身。由于这不是一个关于C语言的文章,所以我们假设读者对C语言有一定的了解。尽管如此,也没有必要对C语言非常精通,使用Objective-C的面向对象的编程和使用ANSI C的面向过程的编程有着很大的差异,所以,即使你不是一个经验丰富的C程序员也不用担心。对象和类对象顾名思义,面向对象的程序都是围绕对象而构建。一个对象把数据和一些对这些数据的操作捆绑在一起,这些操作被称为方法(Methods),而它们操作的那些数据被称

4、为实例变量(instance variables)。本质上讲,对象将一个数据结构也就是一些实例变量和一组方法打包成一个独立的编程单元。例如你正在写一个画图程序,这个程序允许用户创建诸如线、弧线、矩形、文本、位图等,那么你就可以创建关于这些基本图形的很多类。例如一个矩形对象,应该有标识它的位置的实例变量和宽与高的信息,可能还会有别的实例变量来定义它的颜色、矩形是否被填充、直线样式等。这个Rectangle类会有一些方法用来设置一个实例的位置、大小、颜色、填充状态、直线样式,还应该有一个方法用来绘制自己。在Objective-C中,对象的实例变量属于对象的内部数据,通常要访问这些数据只能通过对象的

5、方法,你还可以通过作用域指示符(scope directives)为一个类的子类或别的对象指定访问实例变量的权限。要获取对象别的信息,必须提供一个相应的方法,例如,Rectangle类应该有专用的方法来获取它的大小和位置。而且,对象只能看到为它自己而设计的方法,它不会把别的对象的方法运用到自己身上。就像一个函数隐藏局部变量一样,对象不但隐藏实例变量,还会隐藏方法的实现。Id类型在Objective-C中,对象变量是一种独特的数据类型id类型。这种类型被定义为指向对象的指针。实际是一个指向这种对象的实例变量的指针。就像C语言中的函数和数组一样,对象也是用地址来表示的,所有对象,不管它们的实例变量

6、或方法是什么,都属于一种类型id类型。id anObject;在Objective-C中,id取代了int类型成为默认的数据类型(在标准C里,如函数的返回值,int是默认的返回类型),关键字nil被定义为空对象,也就是值为0的对象,Objective-C其它的基本类型都在obj/objc.h中定义。动态类型匹配id类型是一种灵活的数据类型,除了指示它是一个对象外,并不能提供更多信息。对象并不会完全相同,一个Rectangle对象不会和一个表示位图的对象有相同的实例变量和方法,有时程序可能需要一个对象的更多确切信息,如它包含那些实例变量,可以执行那些操作等。但id指示符却不能为编译器提供这方面的

7、信息,所以每个对象在运行时必须提供这些信息。能够做到这一点都是因为每个对象都有一个isa实例变量来标示这个对象所属于的类。每个Rectangle对象都能告诉运行时系统它是一个矩形,而每个Circle对象能告诉运行时环境它是一个弧形。所有的实例变量和方法都相同的对象都是同一个类的成员。因此,动态类型匹配实际上发生在程序被执行时。不论何时,只要需要,运行时系统就能够查明一个对象到底属于哪个类,而这只需要查询对象的isa实例变量就可以了。这个isa指针还为对象提供了一种称为“自省”(introspection)的功能。编译器会在数据机构中记录关于类定义的信息为运行时环境所用。通过这种机制,你可以判断

8、一个对象是否提供了一个特定的方法,也可以获取这个对象的超类(supperclass)的名字。当然也可以通过静态类型匹配为编译器提供有关对象所属类的信息,类名也可以作为类型名使用。对象消息这一部分讲述发送消息的语法,还包括消息嵌套的表示方法,另外还会涵盖对象的实例变量的“可见性”问题、多态的概念以及动态绑定(dynamic binding)。消息语法要让对象做些事情,你必须向它发送消息,告诉它去执行一个方法。在Objective-C中,消息表达式被包含在一对中括号里:receiver messageReceiver是一个对象,而message告诉这个对象要做什么,具体到代码里,消息就是方法名字和

9、传给这个方法的一些烈参数。一旦一个消息发出,运行时系统会为这个对象选择正确的方法并启动它。例如,下面这条消息告诉myRect对象执行它的display方法:myRect display;当然,方法也可以带参数,下面这个假想的消息告诉myRect把自己定位在坐标(30.0, 50.0)上:myRect setOrigin:30.0 :50.0;这个方法带有两个冒号,其中每个冒号跟一个参数,参数紧随冒号。这个方法使用无标签的参数。无标签参数难以确定参数的目的,因此方法应该用带标签的参数,这些标签可以描述每个参数的含义。参数标签位于冒号前面,如:myRect setWidth:10.0 height

10、:15.0;尽管不太常见,但带有可变数量的参数也是可能的,其余的参数用逗号隔开,在下面的例子中,这个方法传入一个必要参数group和三个可选参数:receiver makeGroup: group, memberOne, memberTwo, memberThree;和标准C的函数一样,方法也可以返回值,下面的例子,如果myRect以填充方式绘制的话为变量设置为YES,否则设置为NO:BOOL isFilled;isFilled = myRect isFilled;注意,一个对象的变量和方法可以同名。消息可以嵌套在另外一个消息中,如:myRect setPrimaryColor: otherR

11、ect primaryColor;Objective-C 2.0还提供了点操作符,提供一种调用对象方法的更加紧凑、方便的语法。给nil发送消息在Objective-C中,给nil发送消息是合法的,它只是在运行时环境中什么都不做,在Cocoa中,有很多地方利用了这种特性。发送给nil的消息带有返回值也是合法的。如果一个方法返回一个对象、任何类型的指针、任何size小于或等于sizeof(void*)的类型,如float、 double、 long double、或者long long,那么给nil发送消息将返回0。如果一个方法返回一个数据结构,那么将这个消息传递给nil将为这个数据结构的每个成员

12、都返回0.0。如果一个方法返回上述类型外的其它类型,那么将这个消息传递给nil,返回值为定义。下面的程序片断展示了向nil发送消息的合法用法:id anObjectMaybeNil = nil;/ this is validif (anObjectMaybeNil methodThatReturnsADouble = 0.0)/ implementation continues.注意:向nil发送消息的行为在Mac OS X v10.5中发生了变化,关于早期版本的行文,请查阅早期文档。接收者的实例变量方法有对接收者实例变量的自动访问权限,你不需要将它们作为参数传递给方法。例如,上述的prima

13、ryColor方法就不带参数,但它能从otherRect对象中获取原始颜色并返回它。每个方法都都知道接收者的实例变量,不需要将它们作为参数。这种约定简化了Objective-C的代码。消息被发送给接收者,就好想信件邮寄到你家中一样,消息将信息从外面传递给接收者,而不需要将接收者是谁再传递给它自己。但一个方法只对接收者的实例变量具有自动访问权限,如果它需要了解别的对象的信息,它就要向这个对象发送相应的消息来或者这些信息,上述的primaryColor和isFilled方法展示的就是这个目的。多态正如以上例子展示的那样 ,Objective-C中消息有着和C语言中的函数调用类似的语法,但是由于方法

14、是属于对象的,消息的行为又不同于C中的函数调用。特别是对象只能执行为它定义过的方法,它不能乱用为别的对象定义的方法,即使别的对象有一个和这个对象相同名方法。这意味着不同对象对相同的消息的响应是不同的,如,为每个对象发送display消息,这些对象会以不同的方式显示自己,这种特征被称为多态。它在面向对象的程序中扮演重要角色。结合动态绑定(dynamic binding),这允许你编写应用于任何不同种类对象的代码,而不用去考虑到底会应用到什么样的对象。这些对象甚至可以是尚未开发的,或者是由别的项目的程序员开发的。如果你写了一个向id类型变量发送display消息的代码,任何拥有display方法的

15、对象都可能成为这个消息的接收者。动态绑定函数调用和消息的一个关键区别是,函数和它的参数是在编译时绑定在一起的,而消息和接收者直到程序运行时,消息被发送才实现这种绑定。因此,响应一个消息的具体方法是在运行时才决定的,而不是在代码被编译的时候。消息启动哪个方法取决于消息的接收者,不同的接收者可能有同名的不同方法实体,这就是多态。编译器要为一个消息找到正确的方法实体,它必须知道这个接收者属于什么对象,也就是它属于什么类。这是一个对象在运行时接收到消息时能够获取的信息,但这些信息不是可以通过代码中的类型声明就能获得的,方法实体的选择发生在运行时。当消息发出之后,运行时环境会查看接收者以及它的和消息同名

16、的方法,它通过名字匹配找到这个接收者的方法,并调用它,并传递一个指向接收者实例变量的指针。消息中的方法名用来“选择”接收者的方法,正是由于这个原因,消息中的方法名常被称作选择器(selector)。方法和消息的动态绑定,以及多态之间的协调合作,给了面向对象编程丰富的灵活性和强大的功能,因为每个对象都可以有一个方法的自己的版本,但仅仅是接收相同消息的对象不同,而这些都可以在程序运行时完成。操作执行基于Application Kit的代码时,用户可以决定哪个对象接收源于菜单命令的消息,如剪切、复制和粘贴,发到任何对象的消息都会控制当前的选择。一个显示文本的对象和一个显示图片的对象对copy消息会有

17、完全不同的响应。而一个矩形和一个表示一系列形状的对象也会有完全不同的响应。类面向对象的程序往往都是基于各种各样的对象而构建,基于Cocoa框架的程序可以使用NSMartrix对象、NSWindow对象、NSDictionary对象、NSFont对象和NSText对象等,还有其它很多。程序还可以使用同一个类的多个对象,如一些NSArray对象或者NSWindow对象。在Objective-C中,通过定义类来定义对象。类是对象的原形定义,在类中声明实例变量,这些变量都是类的成员,它还定义一系列的方法,供这个类的所有对象使用。编译器为每个类定义一个类对象(Class object),这个类对象知道如

18、何构建属于这个类的新对象,由于这个原因,它传统上还被称为工厂对象(Factory object)。类对象是类的编译版本,而它所构建的对象被称为类的实例。在你的程序中真正做工作的是类在运行时对象创建的那些实例。一个类的所有实例有同一套方法,而且有相同一套实例变量。每个对象都有自己的实例变量,但他们却共享这套方法。根据约定,类名以大写字母开头,如Rectangle,而实例变量通常以小写字母开头,如myRect。继承(Inheritance)类的定义通常是添加式的,一个新的类往往都基于另外一个类,而这个新类继承了原来类的方法和实例变量。新类通常简单地添加实例变量或者修改它所继承的方法,它不需要复制继

19、承的代码。继承将这些类连接成一个只有一个根继承关系树。在写基于功能框架的代码时,这个根类通常是NSObject,每个类(除了根类)都有一个超类,而每个类,包括根类都可以成为任何数量子类的超类。图1.1展示了绘图程序中的一些类的继承关系。该图说明Square类是Rectangle类的子类,Rectangle类是Shape类的子类,Shape类是Graphic类的子类,而Graphic类又是NSObject类的子类。继承是累积的,因此Square对象 不但有专门为这个类定义的实例变量和方法,而且还有Rectangle类、Shape类、Graphic类和NSObject类的实例变量和方法。也就是说,

20、一个Square对象不仅仅是一个Square,它还是一个Rectangle,是一个Shape,是一个Graphic,是一个NSObject。因此除NSObject外的每个类都可以看作是另外一个类的特例或修改。每个子类都会修改它所继承的东西,Square类定义了属于自己的最少的东西使它由矩形变成了正方形。在定义类时,通过声明它的超类将它连接到继承关系树中。你定义的每个类都必须是另外一个类的子类,除非你要定义一个根类。有大量的现成的类可以作为定义新类的超类,Cocoa包含了NSObject类和其他以下框架,其中包括超过250个额外的类库。有些框架类定义了几乎你想要的任何东西,但有一些特别的实现留给

21、类子类。因此你可以通过只写少量的代码并重用框架工作人员的工作就能创建出功能丰富的对象。抽象类有些类被设计为不能被实例化,这种类被称为抽象类。它们只用作超类被其它类所继承,这些类往往自身是不完整的,而只是包含了一些有用的代码,这些代码能够被子类所继承,从而减少子类的代码量。NSObject类就是一个重要的抽象类,程序中经常会定义NSObject的子类并使用这些子类的实例,但从来没有直接使用这个类的实例的。但有些抽象类和可以直接使用,例如NSView类是一个可以直接使用的抽象类的例子。抽象类通常包含一些帮助定义应用程序架构的代码,当你定义这些类的子类时,这些子类的实例能很好地适应这种应用程序架构,

22、并能够自动地和别的对象协作。由于抽象类只有定义了子类才能成为一个有用的类,因此它们常常被称为抽象超类。类类型类实际上是一类对象的详细说明,它实际上定义了一个数据类型,这个类型不但基于它所定义的数据结构,还取决于它所定义的一些行为,这些行为就是方法。类名可以出现在任何C语言允许的类型说明符出现的地方,例如作为sizeof操作符的参数:Int i = sizeof(Rectangle);静态类型匹配你也可以使用类名取代id来指定对象的类型,如:Rectangle *myRect; 由于这种声明对象的方式为编译器提供了对象种类的信息,所以被称为静态类型匹配。由于id被定义为指向对象的指针,所以对象被

23、静态匹配成类指针。对象通常都是被定义成指针,这样静态匹配使得指针的含义更加明确,而id却隐藏了这些信息。静态类型匹配使编译器具备了一些类型检查功能,例如如果一个对象接收一个没有定义的消息时,可以发出警告,而如果把对象定义为id,就会放松这种限制。除此之外,它还可以使你的代码更容易理解。尽管这样,它还是无法替代动态绑定的位置。一个对象可以被静态地匹配为它自己的类或者它所继承的类。例如因为继承使得Rectangle成为一种Graphic,所以Rectangle实例可以静态匹配为Graphic类:Graphic* myRect;这样做之所以合法,是因为Rectangle本身就是一种Graphic,但

24、它由于继承了Shape和Rectangle的特性,所以又不止是一个Graphic。为了类型检查,编译器会把myRect当作Graphic,但在运行时环境中,它会被当作Rectangle对待。类型自省实例在运行时可以获取自己的类,NSObject类中定义的isMemberOfClass方法可以检查接收者是不是特定类的实例。如:If (anObject isMemberOfClass :someClass)NSObject类中还定义了另外一个类型省更通用的方法isKindOfClass,用来检查一个对象是否属于某个类或者继承域一个类,如:If (anObject isMemberOfClass :

25、someClass)所有对某个对象能够使用静态匹配的类,接收isKindOfClass消息时都返回YES。而自省又不局限于类型信息,本文档的其它部分,会讨论能够返回对象所属类的方法,以及判断一个对象能否对一个消息作出相应的方法,还有获得其它信息的一些别的方法。类对象一个类的定义会包含丰富的信息,但大部分都是关于这个类的实例的,如:类名及超类关于实例变量的完整描述关于方法名及其参数的说明方法的实现所有这些信息都被编译被保存在一个运行时系统能够访问的数据结构中。编译器创建一个而且只创建一个对象来表达这些信息,那就是类对象。类对象具备访问所有这些信息的途径。虽然类对象保存了类的属性,但它本身并不是一

26、个类的实例。它没有自己的实例变量,而且它也不能执行为类实例设计的方法。然而,类定义可以包含只为类对象使用的方法,这就是类方法,这些方法不同于实例方法(类方法实际上相当于C+中的静态方法译者注)。类对象会继承所有超对象的类方法,就像类实例可以继承所有超类实例的方法一样。在代码中,类名就代表类对象,下面的例子中,Rectangle类使用继承自NSObject的方法返回类的版本号:Int versionNumber = Rectangle version;但只有在作为接收者接收一个消息时,类名才能代表类对象,在别的地方,你必须通过给类实例或给类发送class消息来获得一个类id,如:Id aClas

27、s = anObject class;Id rectClass = Rectangle class;如上例所述,和所有其它的对象一样,类对象可以被转化为id类型,而且类对象还可以更精确地转化为类类型,如:Class aClass = anObject class;Class rectClass = Rectangle class;所有的类都属于类对象,使用Class类型和使用类名进行静态类型匹配是等效的。因此,类对象也像类实例那样,可以进行动态类型匹配、接收消息以及从别的类继承方法。不同之处在于它们是由编译器产生的,没有自己的数据结构,它们是用于运行时系统产生类实例的代理。创建实例类对象的主要

28、用途是用于创建新的实例,下面这个例子告诉Rectangle类创建一个Rectangle实例,并将其赋值给myRect变量:id myRect;myRect = Rectangle alloc;alloc方法会为这个实例的每个变量实例动态分配内存,并将除连接该实例和它的类的isa变量外的所有实例变量全部清零。一个对象要能真正派上用场,它还必须完整地初始化,这就是init方法的作用,通常init方法都紧挨着alloc方法,如:myRect = Rectangle alloc init;在一个实例能够接收消息前,对实例进行这样的初始化是必须的。Alloc方法返回一个新的实例,然后init方法对这个事

29、例进行初始化。每个类对象至少拥有一个方法(如alloc)才能让它具备创建新对象的能力。而每个实例至少一个方法(如init)让它能够有用。而初始化方法通常带有一些参数用来传递特定的值,而且用标签来标示这些参数,例如initWithPositon: size:,但不管怎样,它都是以init开头。用类对象定制类将类作为对象来处理决不是Objevtive-C的突发奇想,这反而是一种有意的做法,而且有时候令人惊讶地对设计有很大帮助。使用类来定制对象是可能的,例如,一个类属于一种开放组合(an open-ended set),例如在Application Kit中,NSMatrix对象可以用特定种类的NS

30、Cell类对象来定制。NSMatrix对象负责创建每个标示它的元素的对象,那么它就可以在matrix对象首次初始化时来创建元素对象,且在添加新的元素时创建新的对象。NSMatrix对象在屏幕上绘制的矩阵可能在运行时增加或减少来响应用户的行为。当矩阵元素增加时,矩阵需要有能力创建新的元素对象,那么需要创建什么种类的对象呢?每个矩阵只能显示一种元素,但却有很多种元素类型。图3-1展示了提供的Application Kit提供的一些元素类,所有这些类都继承于NSCell类:矩阵创建NSCell对象时,应该是显示一堆按钮的NSButtonCell对象,是一些用户可以输入的文本框对象,还以别的种类的NS

31、Cell对象呢?那么NSMatrix对象必须支持任何种类的元素类型,甚至是那些尚未发明的元素类型。一个解决办法是,把NSMatrix定义为抽象类,并要求它的每个用户都定义一个它的子类,并为子类实现一个创建新矩阵元素的方法。由于用户实现这些创建元素的方法,他们当然明白这些元素类对象的类型是正确的。但这种办法实际上要求用户作了本该属于NSMatrix的工作,而其还没有必要地增加了类的数量。由于一个应用程序中可能需要不止一种NSMatrix,而且每个矩阵都有不同的元素类型,这个程序将会堆满大量的NSMatrix子类,只要创建一种新的元素类,你就不得不定义一个新的NSMatrix类。一种更好的方案,实

32、际也是NSMatrix所采取的方案是,允许NSMatrix实例使用一种NSCell类即类对象来初始化。为NSMatrix定义一个setCellClass方法,并为这个方法传递一个NSCell某个子类的类对象,NSMatrix用这个类对象来创建新的元素。myMatrix setCellClass:NSButtonCell class;变量和类对象定义一个类,你可以为它定义实例变量,这个类的每个实例都保留一份这些实例变量的拷贝每个对象管理自己的数据。但是并没有类似实例变量那样的所谓的“类变量”。而且类对象无法访问任何实例的实例变量,它不能初始化、读写这些变量。为了让类的所有实例共享数据,你必须定于

33、外部变量(不是在类中定义的变量)。最简单的办法是像下面的代码片断那样在类的实现文件中声明一个变量:int MCLSGlobalVariable;implementation MyClass/ implementation continues更巧妙的做法是,你可以把变量声明为静态的,并提供类方法来管理它。静态变量会把它的作用域限定在类范围,而且仅限于当前文件的类的实现部分。并不像实例变量那样,静态变量不能被继承,不能被子类直接操作。这种特性常常用于定于类的共享实例(如单一模式,参考“创建单一实例”部分)。static MyClass *MCLSSharedInstance;implementat

34、ion MyClass+ (MyClass *)sharedInstance/ check for existence of shared instance/ create if necessaryreturn MCLSSharedInstance;/ implementation continues静态变量能够帮助类对象提供一种比“工厂模式”创建实例更多的功能。而且它靠自己的能力接近一个完整且通用的对象。类对向可以用来协调它所创建的对象,从已经创建的对象类表分发对象实例,活着管理对应用程序比不可少的别的进程。it can approach being a complete and versa

35、tile object in its own right. A class object can beused to coordinate the instances it creates, dispense instances from lists of objects already created, or manage other processes essential to the application. In the case when you need only one object of a particular class, you can put all the objec

36、ts state into static variables and use only class methods. This saves the step of allocating and initializing an instance.初始化类对象如果类对象不分配实例,它就可以向类的实例那样初始化,尽管程序并不为类对象分配内存,但Objective-C确实也提供了一个初始化类对象的途径,如果类使用静态或全局变量,那么initialize是一个初始化这些变量的好地方。例如,如果一个类拥有一个array实例,那么initialize方法可以创建这个数组,甚至可以创建一两个数组元素。在类接受

37、任何其它消息前,运行时环境会给类对象发送一个initialize消息,当然这发生在它的超类收到initialize消息之后。这就给这个类一个在被使用前建立运行时环境的机会。如果没有初始化工作要做,你就没有必要实现一个initialize方法来响应这个消息。因为继承关系的存在,给一个没有实现initialize的类发送initialize消息,这个消息将会被传递到它的超类,即使超类已经受到这个消息也是如此。例如,A类实现了initialize方法,而类B继承自类A,但类B没有实现initialize方法,在类B接收它的第一个消息之前,运行时环境会为它发送一个initialize消息。但由于类B没

38、有实现initialize方法,类A的initialize方法会被执行,因此,类A需要确保它的初始化逻辑只被执行一次。为了确保初始化逻辑只执行一次,请使用如下的模板实现initialize方法:+ (void)initializestatic BOOL initialized = NO;if (!initialized) / Perform initialization here.initialized = YES;根类方法所有的类和实例,都需要有一个和运行时环境交互的接口,类对象和实例都必须对自己的能力能够完成自省功能,并能够汇报自己在继承关系中所处的位置。NSObject会提供这些接口,所

39、以NSObject不需要实现两次一次为实例提供可以和运行时环境交互的接口,另一次在类对象中复制这些接口。类对象扮演另外一个角色,就是执行根类中定义的实例方法。当类对象接收一个不能响应消息时,运行时环境会判断是否有根实例方法可以响应这个消息。类对象能够执行的唯一的实例方法就是那些在根类中定义的方法,而且还必须是没有类方法能完成这项工作时才会由类方法来完成。有关类方法执行实例方法的特殊能力,请参考有关NSObject类的说明。代码中的类名在代码中,类名职能在两种不同的上下文环境中出现,这两种情况也反映了类名作为数据类型和对象的双重角色:类名可以作为某种对象的类型名,例如:Rectangle * a

40、nObject;这里anObject被明确地定义为指向Rectangle的一个指针。编译器会认为它具有Rectangle的数据结构和Rectangle的实例方法以及Rectangle所继承的方法。静态类型匹配可以让编译器做到更好的类型检查并使得代码更加清晰。但只有实例才能静态地进行类型匹配,类对象不能,因为它们不属于某个类的一种。在发送消息的表达式中,作为消息的接收者,类名代表的是类对象,这种用法在前面的例子中已经多次展示过。只有作为消息接收者,类名才能代表一个类对象。在任何别的上下文环境中,必须要先通过发送一个class消息,获取类对象的id,例如:if ( anObject isKindO

41、fClass:Rectangle class ).It would have been illegal to simply use the name “Rectangle” as the argument. The class namecan only be a receiver.If you dont know the class name at compile time but have it as a string at runtime,NSClassFromString will return the class object:NSString *className;.if ( anO

42、bject isKindOfClass:NSClassFromString(className) ).This function returns nil if the string its passed is not a valid class name.Classnames exist in the same namespace as global variables and function names. A class and a globalvariable cant have the same name. Classnames are about the only names wit

43、h global visibility inObjective-C.定义一个类源文件尽管不是编译器强制的,但接口和实现往往都放在不同的文件中。接口文件必须让每个使用这个类的地方都能访问到。一个文件可以声明或定义不止一个类,当通常情况是,即使不是每个类都有自己的实现文件,至少每个类都有自己的接口文件。保持这些文件独立更能反映它们是相互独立的实体。接口和实现文件通常都以类名来命名,实现文件以.m作为后缀,用来说明这个文件包含Objective-C的源代码;接口文件可以使用任何扩展名,但是因为它们被包含在其它代码文件中,所以这些文件常使用.h作为扩展名。例如Rectangle类可以在Rectangl

44、e.h文件中声明,而在Rectangle.m文件中定义。将类的接口和实现分开非常符合Objective-c程序设计的思路。一个对象是一个自包含的实体,在外部可以被当作“黑盒子”看待。一旦你想好了一个对象如何和程序中的其它元素进行交互,也就是说一旦你定义了这个类的接口,你可以随意更改它的实现,而不会对代码的其它部分造成影响。类接口类的声明以指示符interface开始,并以指示符end结束(注意所有的Objective-C的指示符都以开头)。例如:interface ClassName : ItsSuperclassinstance variable declarationsmethod dec

45、larationsend第一行声明了类名并将这个类和它的超类建立联系。正如“继承”部分所述,超类决定了这个新类在继承关系中所处的位置。如果省略了冒号和超类,这个类就是一个根类,也就是和NSObject平级的一个类。紧接着花括号括起来的部分是实例变量的声明,这是此类每个和实例都包含的部分数据结构。这里是Rectangle类可能包含的实例变量的列表:float width;float height;BOOL filled;NSColor *fillColor;接下来位于花括号后面和end指示符前面的部分声明类方法,可以被类对象使用的方法都已+开头,它们成为类方法。如:+alloc类实例能够使用的方

46、法,称为实例方法,以-开头,如:-(void)display;尽管不常见,但你还是可以定义同名的一个类方法和一个实例方法。而且,方法也可以和实例变量同名,这很常见,特别是方法用来返回这个实例变量的值。如:Circle类有一个radius方法,它正好和radius实例变量搭配。方法的返回值类型采用和标准C相同的语法来声明,如:- (float)radius;参数类型也用同样的方式声明,如:- (void)setRadius:(float)aRadius;如果返回值类型和参数类型没有明确声明,它们会被认为是用默认类型id。前面提到的alloc方法就返回id类型,如果有多于一个的参数,参数和方法名一

47、起声明,它们位于冒号的后面,如:- (void)setWidth:(float)width height:(float)height;参数个数可变的方法用一个逗号和一个省略号来声明,如:- makeGroup:group, .;导入接口任何依赖一个类的代码模块都必须包含这个类的接口定义,这些接口包含了创建这个类的新实例所需的一切,发送一个消息调用这个类的某个方法,或者访问这个类的实例变量等。接口通常都是采用#import指示符来包含,如:#import Rectangle.h这个指示符和#include指示符类似,唯一不同的是它能够确保同一文件不被多次包含。因此在Objective-C中要优先

48、使用#import指令。为了说明一个类的定义继承于另一个类,接口文件都以导入超类的接口文件开头,如:#import ItsSuperclass.hinterface ClassName : ItsSuperclassinstance variable declarationsmethod declarationsend这一约定意味着每个接口文件都间接包含它的所有超类的接口文件。一旦一个代码模块包含了一个类的接口文件,它就获得了这个类所继承的所有接口。引用其它类接口文件声明一个类,并通过导入它的超类,隐式地包含所有被继承的类,从NSObject一直到它的超类。如果接口需要访问不在继承关系中的类,

49、它必须显示地包含它们或者使用class指示符声明它们。如:class Rectangle, Circle;此指示符只是简单地告诉编译器Rectangle和Circle是两个类名,但它并不会导入这两个类的接口。在需要对实例变量、返回值、参数静态类型匹配时接口文件引用类名。如:- (void)setPrimaryColor:(NSColor *)aColor;这个声明就需要引用NSColor类。由于这种声明仅仅简单地将类名作为一个类型而并不需要类接口的更多细节,例如它的方法和实例变量。那么class操作符可以给编译器想要的足够信息。然而在类的接口实际使用的地方,如创建实例或者发送消息时,类接口就必

50、须被导入。通常的做法是,接口文件使用class指令来声明这些类,而在相应的实现文件中导入这个类的接口,因为在创建这些类 的实例以及发送消息时会需要这些接口。class操作符减少了编译器和连接器看到的代码量,因此这是给出类的预先声明的最简单的办法。除了简单,它还能避免导入文件可能带来的隐患。例如,一个类声明了另一个类的静态类型匹配的实例变量,而且这两个接口文件相互导入,那么这两个类都无法编译。接口的角色接口文件的作用是向别的代码模块或别的程序员声明新的类。它包含了使用这个类的所有信息,程序员甚至可以提前制定一个小小的文档。接口文件告诉用户这个类的继承关系,这个类继承和引用的类。接口文件还告诉编译

51、器这个类的一个对象都包含哪些实例变量,还告诉程序员它的子类可以继承哪些变量。尽管实例变量通常被认为是类的实现需要的元素,而不是它的接口,但它们还是必须在接口文件中声明。This is because the compiler must be aware of the structure of an object where its used, not just where its defined. 然而,作为一个程序员,你完全可以忽略你所使用的类的这些实例变量,除非你要定义它的子类。 最后,通过类方法列表,接口文件告诉其它模块可以给这个类的类对象或实例发送什么样的消息。能够对一个类实用的所有方

52、法都在接口文件中定义,类所使用的内部方法可以省略。 类实现类的实现很像声明,它以implementation操作符开始,以end操作符结束:implementation directive and ends with the end directive:implementation ClassName : ItsSuperclassinstance variable declarationsmethod definitionsend当然每个实现文件首先需要导入自己的接口文件。例如Rectangle.m导入Rectangle.h,由于实现不需要重复它所导入的接口,所以它可以简单地将这两部分忽略:

53、超类名实例变量的声明这简化了实现并把主要精力都放在方法的实现上:#import ClassName.himplementation ClassNamemethod definitionsend方法的定义和C语言的函数一样,包含在一对花括号中, 的前面和接口文件中的格式相同,但是没有分号。例如:+ alloc.- (BOOL)isfilled.- (void)setFilled:(BOOL)flag.参数个数可变的方法就像函数一样处理参数,例如:#import .- getGroup:group, .va_list ap;va_start(ap, group);.引用实例变量默认地,在类实例的方

54、法定义中可以访问这个对象的所有实例变量。在这里可以简单地通过名字访问它们。尽管编译器会创建类似C语言中的结构来存储实例变量,但真实的特征是隐含的。你不需要任何的结构体操作符(.或-)来访问这些实例便量。例如下面的方法定义中直接访问接收者的filled实例变量:- (void)setFilled:(BOOL)flagfilled = flag;.不管是接收者还是filled实例变量,都没有被声明为这个方法的参数,但这个实例变量还是在方法的作用域内。方法中的这种简化语法是对编写Objective-C代码的很有意义的简化。如果对象的一个实例变量不是消息的接收者,那么这个对象的类型必须通过静态类型匹配

55、显示地告诉编译器。在访问静态类型匹配的实例变量时需要使用-操作符。例如,假设Sibling类声明了一个静态类型匹配的对象twin作为一个实例变量:interface Sibling : NSObjectSibling *twin;int gender;struct features *appearance;由于这个静态类型匹配的实例变量的作用域是整个类,Sibling方法可以直接对它赋值:- makeIdenticalTwinif ( !twin ) twin = Sibling alloc init;twin-gender = gender;twin-appearance = appeara

56、nce;return twin;实例变量的作用域Although theyre declared in the class interface, instance variables are more a matter of the way a class is implemented than of the way its used. 对象的接口取决于它的方法,而不是内部数据结构。通常方法和实例变量之间都有一种一对一的关系,例如:- (BOOL)isFilledreturn filled;但并不一定都是如此,有些方法可能返回并不存储于实例变量中的信息,也有一些实例变量存储对象不会获取的信息。

57、由于类经常会被修改,实例变量可能发生变化,但方法可能保持不变。由于消息是和类实例进行交互的媒介,这种改变通常不会影响接口。为了隐藏对象的数据,编译器会规定实例变量的作用域。但为了提供更高的灵活性,编译器也允许程序员来为实例变量定义三个级别的作用域。每个级别都用一个编译器操作符来标记:操作符含义private实例变量只能在定义它的类中被访问protected实例变量可以在定义它的类及其子类中被访问public实例变量可以在任何地方被访问package在64位机上,在实现类的镜像文件中,package 的作用相当于public,而在这个镜像文件的外部,它的作用相当于private。这就相当于修饰变

58、量或函数的private_extern。实现类的镜像文件外的任何地方试图访问这种实例变量都将得到一个连接错误。这种特性在框架类中很有用,因为在这种类中,private太严格,而public又太宽松。图2-1展示了实例变量的作用域:一个指示符作用于它后面的所有实例变量,直到遇到下一个指示符,或者到实例变量解表的最后。在下面的例子中,age和evaluation变量的作用域是private,name,job,wage的作用域是protected,boss的作用域是public:interface Worker : NSObjectchar *name;privateint age;char *ev

59、aluation;protectedid job;float wage;publicid boss;默认地,所有未标记的实例变量的作用域都是protected。不管如何标记,所有的实例变量在类定义中都是可见的。例如,上述类定义了job实例变量,在方法定义中可以 引用它:- promoteTo:newPositionid old = job;job = newPosition;return old;很明显,如果一个类不能访问它自己的实例变量,那么这个变量无论如何都没有任何用处。 通常,一个类可以访问它所继承的所有实例变量。访问实例变量的能力通常伴随变量而继承。让类的所有实例变量都能被访问很有意义

60、,尤其你把类的实现当作它所继承的类的精心设计品时。上面的promoteTo方法在所有从Worker类继承了job变量的子类中都有定义。然而,在继承一个类时,也有理由限制对实例变量的直接访问。 在子类访问它所继承的变量时,声明这个变量的类和自己的实现绑定在一起。在此后的版本中,可能会删除这个变量或者改变它的角色。而且,如果子类访问一个它所继承的变量并改变它的值,这可能无意地为定义这个变量的类带来bug,尤其是如果这个变量影响类的内部依赖关系时。为了将实例变量的作用域限定在定义它的类中,你必须使用private指示符。被这个指示符标记的变量,只能通过public方法才能被子类所访问。另一个极端是,

61、把变量标记为public,可是让这个变量在任何地方能被访问,甚至在继承或定义这个变量的别的类中。通常,为了获取保存在实例变量中的信息,其他对象必须一个消息,然而,public变量可以在任何地方被访问,就好像它是C结构体的一个成员,例如:Worker *ceo = Worker alloc init;ceo-boss = nil;但是这种情况下,这个类必须使用静态类型匹配。将实例变量标记为public破坏了一个对象隐藏数据的能力,它和面向对象程序设计的数据封装思想背道而驰,因此,如果不是出于特定目的应避免使用这种指示符。类别和类扩展类别允许你为现存的类,甚至是一个你不具有代码的类添加新方法。这种

62、特性允许你极大地扩展现存类的功能,而不需要对其进行子类化。使用类别,你可以将类的实现放在多个文件中,类扩展允许声明额外的API,而这种扩展不需要再interface代码块中。为类添加方法你可以在接口文件中通过类别名(category name)声明新方法并在实现文件中使用同一个类别名来添加新方法。类别名指出,添加到类的方法在别处定义。然而我们不能通过类别名为类添加新的实例变量。类别添加的新方法称为类的一部份。例如,通过类别为NSArry添加的方法也存在于它的方法列表中。而为NSArry子类添加的新方法, NSArry类是不具有的。类别方法可以像为这个类定义的其它方法一样做任何事,在运行时环境中

63、,它们没有任何区别。通过类别为类添加的方法和别的方法一样会被它的子类所继承。类别接口的定义看起来很像类接口定义,而不用的是类别名使用圆括号列出,它们位于类名后面。类别必须导入它所扩展的类的接口文件。例如:#import ClassName.hinterface ClassName ( CategoryName )/ method declarationsend和通常一样,类的实现也要导入它的接口文件,一个常用的命名约定是,类别的基本文件名是这个类别扩展的类的名字后面紧跟一个+号,再跟类别名。因此一个名字为ClassName+CategoryName.m的实现文件看起来就像这样:#import ClassName+CategoryName.himplementation ClassName ( CategoryName )/ method definitionsend注意,类别并不能为类声明新的实例变量,它只包含方法。然而,在类作用域中的所有实例变量,都能被这些类别方法所访问。它们包括为类声明的所有实例变量,甚至那些被private修饰的变量。可以为一个类添加多少个类别并没有限制,但每个类别名必须不同,而且每个类别都必须声明并定于一套不同的方法。类别的使用你可以使用类别扩展在别处实现的类,例如,你可以为Co

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!