对象的初始化
对象的初始化方法一般都如下:
-(id)init { self=[super init]; if(self){ ... } return self; }
这个方法首先会调用父类的初始化方法,这使得继承的实例变量能够正常初始化。必须将父类init方法的执行结果赋值给self,因为初始化过程改变了对象在内存中的位置(意味着引用将要改变)。这里返回id类型,是编写可能会被继承的类的init方法的一般规则,因为子类的对象并不等同于父类,init的返回结果会通过子类的self=[super init]赋值给子类对象,所以不能硬编码返回类型。
作用域
实例变量的作用域只限于该类定义的实例方法,因此,任何实例方法都能直接通过变量名来访问该类的实例变量而无须进行特殊操作。
在接口中声明的实例变量可通过子类进行继承,继承来的实例变量同样可以通过变量名在该子类定义的方法中直接访问。
在接口部分声明实例变量时,可以通过以下指令来精确控制变量作用域:
@protected 指令后的实例变量可被该类及任何子类中定义的方法直接访问。(接口部分定义的实例变量默认就是这种作用域)
@private 指令后的实例变量只能被定义在该类的方法直接访问,子类中定义的方法不可以。(实现部分定义的实例变量默认是这种作用域)
@public 指令后的实例变量可被该类以及其他类或模块中定义的方法直接访问。
@package 对于64位映像,可在实现该类的映像中的任何地方访问这个实例变量。
全局变量
在程序的开始处(所有方法、类定义和函数定义之外)定义的变量,在模块(模块是指包含在一个源文件中任何数目的方法或者函数定义)中的任何位置都可以引用这个变量的值。确切地说,这不仅将该变量定义为全局变量,而且将其定义为外部全局变量。
外部变量是可被任何其他方法或函数访问和更改其值的变量。在需要访问外部变量的模块中,变量声明和普通方式一样,只是需要在声明前加上关键字extern。这就告知系统,要访问其他文件中定义的全局变量。
比如:
extern int gNum;
现在,包含前面这个声明的模块就可以访问和改变gNum的值。同样,通过在文件中使用类似的extern声明,其他模块也可以访问gNum的值。
使用外部变量时,必须遵循:变量必须定义在源文件中的某个位置。即在所有的方法和函数之外声明变量,并且前面不加关键字extern。如:
int gNum;
当然这里可以选择为这个变量赋初值,这就称为定义该变量了。
确定外部变量的第二种方式是在所有函数之外声明变量,在声明前加上extern,同时显示地指派初始值。如:
extern int gNum=0;
这里可行,但不是推荐的。此时编译器会给出警告,提示你将变量声明为extern并同时为其赋值。这是因为使用关键字extern标明这条语句是变量的声明而不是定义。声明是不会分配变量的存储空间的,定义才会引起变量的存储空间分配。这里强行将声明当做定义处理,违背了这个规则。
处理外部变量时,变量可以在许多地方声明为extern,但是只能定义一次,具体示例如下:
//main.m #import "Foo.h" int gGlobalVar=5; int main (int argc, char *argc[]) { Foo *myFoo = [[Foo alloc] init]; NSLog (@"%i", gGlobalVar); [myFoo setgGlobalVar]; NSLog (@"%i", gGlobalVar); return 0; } //Foo.m -(void) setgGlobalVar { extern int gGlobalVar; gGlobalVar=100; } //程序结果 5 100
可以在文件的开头统一进行一次extern声明,之后就可以在多个方法中访问这个变量了。
静态变量
外部变量与数据封装原则和面向对象编程技术是相违背的,更好地是将访问限制在setter和getter方法中,将实例变量隐藏起来。
如上所述,在方法之外定义的变量不仅是全局变量,也是外部变量。如果希望变量是全局而非外部,即在特定模块(文件)内全局,就需要使用关键字static。如果
static int gNum=0;
声明在任何方法(或函数)之外,那么在该文件中,所有位于这条语句之后的方法或者函数都可以访问gNum的值,而其他文件中的方法和函数则不可以,这就是静态变量。
静态变量不是实例变量,这一点很重要,因为实例方法可以访问实例变量,而类方法是不能访问实例变量的,但是类方法可以访问静态变量。比如类的分配器方法,它要记录类已经分配空间的对象数目。设置一个静态变量,分配器方法就可以直接访问它,而类的用户并不知道这个变量,因为它是定义在实现文件中的静态变量,作用域是文件内部,因此,用户不能直接访问该变量,这也就不违背数据封装的概念,如果需要从类之外访问该变量,则可以编写一个方法来获取该变量的值。
枚举数据类型
Objective-C中可以将一系列值指派给一个变量,就是枚举类型。
定义:枚举类型的定义以关键字enum开头,之后是枚举数据类型得名称,然后是标识符序列(包括一对花括号),它们定义了可以给该类型指派的所有的允许值(理论如此,实际上如果指派其他值,编译器也不会警告)。例如:
enum flag {true,false}
声明:要声明一个enum flag类型得变量,仍需要用到关键字enum,之后是枚举类型名称,最后是变量序列,如:
enum flag Flag1, Flag2
能够指派给Flag1和Flag2的值只有true和false。
如果希望一个枚举类型标识符对应一个特定整数,可以在定义数据类型时给该标识符指定整数,列表中随后出现的枚举标识符被依次赋以整数值,从指定的整数值加1开始,如:
enum d {a,b,c=10,d,e}
此时a=0,b=1,c=10,d=11,e=12。当然,枚举标识符可以用相同的整数值。
定义枚举类型时,也可以省略数据类型名称,如:
enum {east,west,south,north} direction
使用枚举数据类型时,尽量不要依赖枚举值被作为整数这个事实,而是把它们当做独立的数据类型。枚举类型提供了一种方法,使你能把整数值和有象征意义的名称对应起来。代码块中定义的枚举类型作用域限于块内部,程序的开始及所有块之外定义的枚举类型对于该文件是全局的。
枚举数据类型
Objective-C中允许为数据类型另外指派一个名称,通过typedef实现。如:
typedef int myInt; // myInt a,b;
typedef Number *myNumber;
//
myNumber n1,n2;
要使用typedef定义一个新类型名,可以按如下步骤:
1)像声明所需类型的变量那样编写一条语句。如:int a;
2)在通常应该出现声明的变量名的地方,将其替换为新的类型名。如 int myInt;
3)在语句的前面加上关键字typedef。