Objective-C-2.0程序设计-第10章-变量和数据类型.ppt_第1页
Objective-C-2.0程序设计-第10章-变量和数据类型.ppt_第2页
Objective-C-2.0程序设计-第10章-变量和数据类型.ppt_第3页
Objective-C-2.0程序设计-第10章-变量和数据类型.ppt_第4页
Objective-C-2.0程序设计-第10章-变量和数据类型.ppt_第5页
免费预览已结束,剩余32页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第10章 变量和数据类型,第10章 变量和数据类型,10.1 类的初始化,前面已经出现过这种模式:使用以下常见序列,分配对象的新实例,然后对它初始化: Fraction *myFract = Fraction alloc init; 调用这两个方法之后,通常向这个新对象指派一些值,如下所示: myFract setTo: 1 over: 3; 初始化对象之后为其设置初值的过程通常可以合并到一个方法中。例如,你可以定义一个initWith:方法,它初始化一个分数,并将其分子和分母设置为两个给定的参数。 包含很多方法和实例变量的类通常还有几个初始化方法。例如,Foundation框架中的NSArr

2、ay类包含了以下6中初始化方法: initWithArray: initWithArray:copyItems: initWithContentsOfFile: initWithContentsOfURL: initWithObjects: initWithObjects:count:,10.1 类的初始化,常见的编程习惯是类中所有初始化方法都以init开头。在编写初始化方法时,应该遵循以下两个策略。 如果你的类包含多个初始化方法,其中一个就应该是指定的初始化方法,其他所有初始化方法都应该使用这个方法。通常,它是最复杂的初始化方法(一般是参数最多的初始化方法)。通过创建指定的初始化方法,可以把

3、大部分初始化代码集中到单个方法中。然后,任何人想要从该类派生子类,就可以重载这个指定的初始化方法,以便保证正确的初始化新的实例。 一定要恰当地初始化任何继承来的实例变量。最简单的方式就是首先调用父类指定的初始化方法,大多数情况下是init方法,然后可以初始化自己的实例变量。 基于这个讨论,Fraction类的初始化方法initWith:可能如下所示: -(Fraction *) initWith: (int) n: (int) d self = super init; if (self) self setTo: n over: d; return self; ,10.1 类的初始化,这个方法首

4、先调用父类的初始化方法,也就是NSObject的init方法。初始化的结果需要指派回self,因为初始化方法有权更改或移动内存中的对象。 完成Super的初始化之后,使用setTo:over:方法设置Fraction的分子和分母。和其他初始化方法一样,希望由你返回初始化的对象,在这里你就是这样做的。 代码清单10-1测试了新的初始化方法initWith:。,10.1 类的初始化,类的初始化方法init模板: -(instancetype)init: self = super init; if (self) /初始化代码 return self; ,10.1 类的初始化,代码清单10-1 - #

5、import “Fraction.h” int main (int argc, char *argv) NSAutoreleasePool * pool = NSAutoreleasePool alloc init; Fraction *a, *b; a = Fraction alloc initWith: 1: 3; b = Fraction alloc initWith: 3: 7; a print; b print; a release; b release; pool drain; return 0; ,输出: 1/3 3/7,第10章 变量和数据类型,10.2 作用域回顾,可以使用几

6、种方式影响程序中变量的作用域。可以改变实例变量以及定义在函数外部或内部的普通变量的作用域。在下面的讨论中,我们使用术语模块(module)来引用包含在一个源文件中任何数目的方法或者函数定义。,10.2.1 控制实例变量作用域的指令,目前,你知道实例变量的作用域只限于为该类定义的实例方法。因此,任何实例方法都能直接通过变量名来访问该类的实例变量,而无需特别的操作。 在接口部分声明实例变量时,可以把以下三个指令放在实例变量之前,以便更精准的控制它们的作用域,它们是: protected这个指令后面的实例变量可被该类及任何子类中定义的方法直接访问。这是默认情况。 private这个指令后面的实例变量

7、可被定义在该类的方法直接访问,但不能被子类中定义的方法访问。 public这个指令后面的实例变量可被该类中定义的方法访问,也可被其他类或模块中定义的方法直接访问 package对于64位图像,可以在实现该类的图像的任何地方访问这个实例变量。,10.2.1 控制实例变量作用域的指令,如果要定义一个名为Printer的类,它包含两个私有实例变量pageCount和tonerLevel,并且只有Printer类中的方法才能直接访问它们,那么可以如下使用接口部分: interface Printer: NSObject private int pageCount; int tonerLevel; pr

8、otected / other instance variables . end,10.2.2 外部变量,如果在程序的开始处(所有方法,类定义和函数定义之外)编写一下语句: int gMoveNumber = 0; 那么这个模块中的任何位置都可以引用这个变量的值。在这种情况下,我们说gMoveNumber 被定义为全局变量。为了向阅读程序的人说明变量的作用域,按照惯例,用小写的g作为全局变量的首字母。 实际上,这样的定义使得其他文件也可以访问变量gMoveNumber 的值。确切地说,前面的语句不仅将gMoveNumber 定义为全局变量,而且将其定义为外部全局变量。 外部变量是可被其他任何方

9、法或函数访问和更改其值的变量。在需要访问外部变量的模块中,变量声明和普通方式一样,只是需要在声明前加上关键字extern。这就告知系统,要访问其他文件中定义的全局变量。下面这个例子说明如何将gMoveNumber 声明为外部变量: extern int gMoveNumber; 现在,包含前面这个声明的模块就可以访问和改变gMoveNumber的值。同样,通过在文件中使用类似的extern声明,其他模块也可以访问gMoveNumber的值。,10.2.2 外部变量,使用外部变量时,必须遵循下面这条重要原则:变量必须定义在源文件中的某个位置。这是通过在所有方法和函数外部声明,并且前面不加关键字e

10、xtern,如下所示: int gMoveNumber; 定义外部变量的第二种方式是在所有函数之外声明变量,在声明前面加上关键字extern,同时显示地位变量指派初始值,如下所示: extern int gMoveNumber = 0; 然而这并不是首选的方法。编译器将给出警告消息:提示你已将变量声明为extern,并同时为变量赋值。这是因为使用关键字extern表明这条语句是变量的声明而不是定义。记住,声明不会引起分配变量的存储空间,而定义会引起变量的存储空间的分配。 处理外部变量时,变量可以在许多地方声明为extern,但是只能定义一次。 通过观察一个小程序例子来说明外部变量的用法。假设我

11、们定义了一个名为Foo的类,并将以下代码键入一个名为main.m的文件中。,10.2.2 外部变量,#import “Foo.h” int gGlobalVar = 5; int main (int argc, char *argc) NSAutoreleasePool * pool = NSAutoreleasePool alloc init; Foo *myFoo = Foo alloc init; NSLog (”%i “, gGlobalVar); myFoo setgGlobalVar: 100 NSLog (”%i”, gGlobalVar); myFoo release; poo

12、l drain; return 0; ,10.2.2 外部变量,在前面的程序中,gGlobalVar定义为全局变量,因此任何方法(或函数)只要正确地使用extern声明,都可以访问它。假设Foo方法setgGlobalVar:如下所示: -(void) setgGlobalVar: (int) val extern int gGlobalVar; gGlobalVar = val; 该程序在终端生成以下结果: 5 100 如果有很多方法需要访问gGlobalVar的值,只在文件的开始进行一次extern声明将比较简单。但是如果只有一个或少数几个方法要访问这个变量,就应该在其中每个方法中单独进行

13、extern声明。这样将使程序的组织结果更加清晰,并且使实际使用变量的不同函数单独使用这个变量。注意,如果变量定义在包含访问该变量的文件中,那么不需要单独的extern声明。,10.2.3 静态变量,前面所示的例子与数据封装原则以及良好的面向对象编程技术相违背。然而,可能需要下面这种变量:它们的值在经过不同的方法调用时是共享的。虽然在Foo类中将gGlobalVar定义为实例变量似乎也不太合理,但是,更好的方法可能是通过将访问限制在类中定义的setter和getter方法中,将实例变量“隐藏”在Foo类中。 现在,你知道在方法之外定义的变量不仅是全局变量而且是外部变量。然而,在很多情况下你想要

14、将变量定义为全局变量但不是外部变量。换句话说,希望定义的全局变量只在特定模块中是全局的。这种变量在下面的情况中很有意义:除了特定类中的方法,再也没有其他方法需要访问这个特定变量。要做的这一点,可以在包含这个特定类实现的文件中将该变量定义为static。,10.2.3 静态变量,如果语句 static int gGlobalVar = 0; 声明在任何方法之外,那么在该文件中,所有位于这条语句之后的方法都可以访问gGlobalVar的值,而其他文件中的方法和函数则不行。 你会想起类方法不能访问实例变量。然而,可能希望类的方法可以设定和访问一些变量。简单的例子是类的分配器方法,它要记录类已经分配空

15、间的对象数目。实现这项任务的方式是在类的实现代码文件中设定静态变量。由于这个变量不是实例变量,所以分配器方法可以直接访问它。类的用户不用知道这个变量。因为它是定义在实现文件中的静态变量,作用域只是文件内部。因此,用户不能直接访问该变量,也就没有违背数据封装的概念。如果需要从类之外访问该变量,则可以编写一个方法来获取该变量的值。 代码清单10-2对Fraction类定义进行了扩充,增加了两个新方法。allocF类方法分配一个新的Fraction对象,同时记录分配了多少Fraction,count方法则返回这个数的值。注意,后者也是类方法,也可以作为实例方法实现,然而,与向类的特定实例发送消息相比

16、,询问类已经分配了多少实例更有意义。,10.2.3 静态变量,下面是需要在实现文件Fraction.m中加入的代码。 static int gCounter; implementation Fraction +(Fraction *) allocF extern int gCounter; +gCounter; return Fraction alloc; +(int) count extern int gCounter; return gCounter; / other methods from Fraction class go here . end,注意:重载alloc并 不是好的编程实

17、践, 因为这个方法处理 内存的物理分配。你 不应该涉足,10.2.3 静态变量,代码清单10-2测试这个新方法 代码清单10-2 - #import “Fraction.h” int main (int argc, char *argv) NSAutoreleasePool * pool = NSAutoreleasePool alloc init; Fraction *a, *b, *c; NSLog (”Fractions allocated: %i”, Fraction count); a = Fraction allocF init b = Fract on allocF init c

18、 = Fraction allocF init; NSLog (”Fractions allocated: %i”, Fraction count); a release; b release; c release; pool drain; return 0; ,输出: Fractions allocated: 0 Fractions allocated: 3,第10章 变量和数据类型,10.3.1 auto,这个关键字用来声明一个自动局部变量,与static相反。这是函数或方法内部变量的默认声明方式,并没有人使用它。下面是一个例子: auto int index; 该语句声明index为一个

19、自动局部变量,意味着在进入该块时,自动为它分配存储空间,并在退出该块时自动解除分配。因为在块中是默认的,因此语句 int index; 和语句 auto int index; 是等效的。 静态变量是有默认的初始值0,而自动变量没有默认的初始值。除非显示地给自动变量赋值,否则它们的值是不确定的。,10.3.2 const,编译器允许你给对程序中值不变的变量设置const特性。这样,就告诉编译器,指定的变量在程序运行期间都有恒定的值。在初始化变量后,如果尝试给const变量指派一个值,或视图将其增1或减1,编译器就会给出警告消息。局一个const特性的例子,代码 const double pi =

20、 3.141592654; 声明了一个const变量pi。这就告诉编译器,程序不会修改该变量。当然,因为随后不能更改const变量的值,因此必须在定义变量时就进行初始化。,10.3.3 volatile,这个类型和const正好相反。它明确告诉编译器,指定类型变量的值会改变。在语言中加入这个关键字是为了防止编译器优化掉看似多余的变量赋值,同时避免重复地检查值没有变化的变量。I/O端口就是一个很好的例子,这涉及到对指针的理解。 加上在程序中,将输出端口的地址存储在一个名为outPort的变量中。如果要向这个端口写两个字符,可能会写出下面的代码: *outPort = O; *outPort =

21、N; 第一行表示在outPort指定的内存地址存储字符O,第二行表示在同一位置存储字符N。一个智能的编译器可能发现对同一地址进行了两次连续的赋值。因为outPort在这两者之间并没有被修改,所以编译器将第一个赋值语句从程序中删除。要防止这种情况发生,应该将outPort声明为一个volatile变量: volatile char *outPort;,第10章 变量和数据类型,10.4 枚举数据类型,Objective-C语句使得你可以将一系列值指派给一个变量。枚举数据类型的定义以关键字enum开头,之后是枚举数据类型的名称,然后是标识符序列,它们定义了可以给该类型指派的所有允许值。例如: en

22、um flag false, true ; 定义了一个数据类型flag。从理论上说,在程序中这个数据类型只能指派true和false两种值,不能指派其他的值。遗憾的是,即使违背了这个规则,Objective-C编译器也不会发出警告消息。 要声明一个enum flag类型的变量,仍需要关键字enum,之后是枚举类型名称,最后是变量序列。所以语句 enum flag endOfData, matchFound; 定义了两个flag类型的变量endOfData和matchFound。能指派给这两个变量的值只有true和false。,10.4 枚举数据类型,如果希望一个枚举标识符对应一个特定的整数值,

23、那么可以在定义数据类型时给该标识符指定整数值。在列表中依次出现的枚举标识符被指派了特定整数值开始的序列数。 在定义 enum direction up, down, left = 10, right ;中,定义了一个保护up,down,left和right的枚举数据类型direction。因为up在序列中位于首位,所以编译器给其赋值为0,down为1;由于left明确被赋值为10,所以它的值就是10;right的值有列表中前一个enum的值递增得到,因此值为11. 枚举标识符可以共享相同的值。如: enum boolean no = 0, false = 0, yes = 1, true =

24、1 ; 下面定义了类型enum mouth: enum month january = 1, february, march, april, may, june, july, august, september, october, november, december ; Objective-C编译器实际上将枚举标识符作为整型常量来处理。thismouth的值是整数2. enum month thisMonth; . thisMonth = february;,10.4 枚举数据类型,代码清单10-3 - int main (int argc, char *argv) NSAutoreleas

25、ePool * pool = NSAutoreleasePool alloc init; enum month january = 1, february, march, april, may, june, july, august, september, october, november, december ; enum month amonth; int days; NSLog (”Enter month number: “); scanf (“%i”, ,10.4 枚举数据类型,case april: case june: case september: case november:

26、days = 30; break; case february: days = 28; break; default: NSLog (”bad month number”); days = 0; break; if ( days != 0 ) NSLog (”Number of days is %i”, days); if ( amonth = february ) NSLog (”.or 29 if its a leap year”); pool drain; return 0; ,输出: Enter month number: 5 Number of days is 31,输出: Ente

27、r month number: 2 Number of days is 28 .or 29 if its a leap year,10.4 枚举数据类型,可以明确的给枚举类型的变量指派一个整数值,这应该使用类型转换运算符 。因此,如果monthValue的值是6,那么表达式 lastMonth = (enum month) (monthValue - 1); 是允许的。 使用包含枚举数据类型的程序时,尽量不要依赖枚举值被当做整数这个事实。相反,应该尽量把它们当做独立的数据类型。 在定义枚举数据类型时,也允许有所变化:可以省略数据类型的名称,定义该类型时,以将变量声明为特定枚举数据类型中的一个。

28、举出一个同时展示这两种选择的例子,语句: enum east, west, south, north direction; 定义了一个(未命名的)枚举数据类型,它包含的值为east,west,south和north,同时还声明了该类型的变量direction。,第10章 变量和数据类型,10.5 typedef语句,Objective-C提供了一种能力,编程者可以为数据类型另外指派一个名称。这是通过所谓的typedef语句实现的。语句 typedef int Counter; 定义名称Counter等价于Objective-C数据类型int。随后的变量就可以声明为Counter类型,如在一下语

29、句中: Counter j, n; Objective-C编译器实际上是将变量j和n的声明当做前面显示的普通整型变量。 在这种情况下使用typedef语句的主要好处是增加了变量定义的可读性。从j和n的定义中就可以清晰地看出这些变量在程序中的目的。 下面的typedef将一个名为NumberObject的类型定义为Number对象: typedef Number *NumberObject; 随后将一些变量声明为NumberObject类型,如在语句 NumberObject myValue1, myValue2, myResult; 中,它们的使用方式和以常规方式在程序中声明一样,如下所示:

30、Number *myValue1, *myValue2, *myResult;,10.5 typedef语句,Objective-C提供了一种能力,编程者可以为数据类型另外指派一个名称。这是通过所谓的typedef语句实现的。语句 typedef int Counter; 定义名称Counter等价于Objective-C数据类型int。随后的变量就可以声明为Counter类型,如在一下语句中: Counter j, n; Objective-C编译器实际上是将变量j和n的声明当做前面显示的普通整型变量。 在这种情况下使用typedef语句的主要好处是增加了变量定义的可读性。从j和n的定义中就

31、可以清晰地看出这些变量在程序中的目的。 下面的typedef将一个名为NumberObject的类型定义为Number对象: typedef Number *NumberObject; 随后将一些变量声明为NumberObject类型,如在语句 NumberObject myValue1, myValue2, myResult; 中,它们的使用方式和以常规方式在程序中声明一样,如下所示: Number *myValue1, *myValue2, *myResult;,10.5 typedef语句,Foundation框架在它的一个头文件中使用typedef对NSComparisonResult

32、进行了如下定义: typedef enum _NSComparisonResult NSOrderedAscending = -1, NSOrderedSame, NSOrderedDescending NSComparisonResult; Foundation框架中一些用于比较的方法会返回一个该类型的值。例如,Foundation的字符串比较方法名为compare:,它在完成两个NSString对象字符串的比较之后返回一个NSComparisonResult类型的值。该方法的声明如下: -(NSComparisonResult) compare: (NSString *) string; 要测试两个名为userName和savedName的NSString对象是否相等,可以在程序里编写一下代码: if ( userName co

温馨提示

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

评论

0/150

提交评论