初始化方法
初始化对象和设置初始值的过程通常可以合并到一个方法中,常见变成习惯是类中所有的初始化方法都以init开头。
包含很多方法和实例变量的类通常还有几个初始化方法。例如Foundation框架中的NSArray类包含6个额外的初始化方法:
initWithArray:、initWithArray:copyItems:、initWithContentsOfFile:、initWithObjects:、initWithObjects:count:。
可使用myArray[[NSArray alloc] initWithArray: myOtherArray]; 完成数组的空间分配和初始化工作。
编写初始化方法时应遵循两个策略:
1. 如果希望在类对象初始化时做一些事情(如Rectangle类初始化时需要为矩形指定XYPoint原点),可以通过重载init方法达到目的。
//重载init方法标准模板 - (instancetype) init { self = [super init]; //调用父类初始化方法,使继承的实例变量能够正常初始化 //注意必须将父类init方法的执行结果赋值给self,因为初始化过程改变了对象在内存中的位置(引用将要改变) if(self) { //如果父类初始化成功,返回值将是非空的 //自定义初始化代码,通常可以再这个位置创建并初始化实例变量 } return self }
2. 如果你的类包含多个初始化方法,其中一个就应该是指定的(designated)初始化方法,通常是最复杂的初始化方法(一般是参数最多的初始化方法),并且其他所有初始化方法都应该使用这个方法。可以把大部分初始化代码几种到单个方法中,任何人想要从这个类派生子类,都可以重载这个指定的初始化方法,保证正确地初始化新的实例。
例:
//为Fraction类添加初始化方法initWith:over: -(Fraction *)initWith:(int)n over:(int)d { self = [super init]; if (self) [self setTo: n over: d]; return self; } //使用初始化方法initWith:over: a = [[Fraction alloc] initWith:1 over:3]; b = [[Fraction alloc] initWith:3 over:7];
为使用指定的初始化规则,需要修改Fraction类的init方法:
-(instancetype) init { //返回为instancetype类型,是编写可能会被继承的类init方法的一般规则 return [self initWith:0 over:0]; }
外部变量:可以被其他任何方法或函数访问和更改的值,只要在需要访问外部变量的模块中用关键字extern声明变量,告知系统要访问其他文件中定义的全局变量。如:
int gMoveNumber = 0; /*变量必须不加关键字extern定义在源文件中的某个位置(在所有方法和函数外)*/ extern int gMoveNumber; //包含该声明的模块可以访问和改变gMoveNumber的值
如果很多方法需要访问该全局变量的值,则只在文件开始进行一次extern声明比较简便;如果只有一个或少数几个方法要访问该全局变量,则应该在其中的每个方法中单独进行extern声明(程序组织结构更清晰、不同函数可以单独使用这个变量);如果变量定义在包含访问这个变量的文件中,则不需要单独进行extern声明。
静态变量:如果希望定义的全局变量只在特定模块(文件)中是全局的,即除特定类中的方法外,再没有其他方法能够访问这个特定变量,可以在包含这个特定类的实现文件中将该变量定义为static。
如:
static int gGlobal; /*如果该语句声明在任何方法(或函数)之外,则文件中所有位于该语句之后的方法或函数都可以访问gGlobalVar的值,而其他文件中的方法和函数则不行。*/
可以通过在类的实现代码文件中设定静态变量,记录类已经分配空间的对象数目。如:
在Fraction.h中添加两个新的类方法声明:
+(Fraction *) allocF; +(int) count;
在实现文件Fraction.m中加入定义:
static int gCounter; @implementation Fraction { int numerator; int denominator; } @synthesize numerator, denominator; /*allocF类方法分配一个新的Fraction对象,同时记录分配了多少Fraction*/ +(Fraction *) allocF { extern int gCounter; ++gCounter; return [Fraction alloc]; } /*count类方法返回记录的数值*/ +(int) count { extern int gCounter; return gCounter; } ... @end
在main.m中测试:
Fraction *a, *b, *c; NSLog(@"Fractions allocated: %i", [Fraction count]); //output: 0 /*程序开始时gCounter自动置零,如果需要设定特定计数可在类中添加setter方法*/ a = [[Fraction allocF] init]; b = [[Fraction allocF] init]; c = [[Fraction allocF] init]; NSLog(@"Fractions allocated: %i", [Fraction count]); //output: 3
枚举数据类型
enum flag {false, true} //定义数据类型flag,只能赋为true或false enum flag endofDate, matchFound; /*声明enum flag类型的变量 (关键字enum-枚举类型名称-变量序列)*/
如果希望一个枚举标识符对应一个特定整数值,可在定义数据类型时给标识符指定整数值,列表中随后出现的枚举标识符依次+1赋整数值。如:
enum direction {up, down, left = 10, right}; //up=0, down=1, left=10, right=11
枚举标识符也可以共享相同的值。
Objective-C编译器实际上将枚举标识符作为整型常量处理。如:
enum month {January = 1, feburary, march, april, may, june, july, august, september, october, november, december}; enum month thisMonth; thisMonth = feburary; //赋给thisMonth的值为整数2
但尽量不要依赖枚举值被作为整数的事实,应该将枚举数据类型当做独立的数据类型。
定义枚举类型时可以将一个整数类型和一个枚举名称对应起来。如:
enum iPhoneModels : unsigned short int {iPhone, iPhone3G, iPhone3GS, iPhone4, iPhone4s, iPhone5, iPhone5C, iPhone5S); /*使用unsigned short int作为相关数据类型*/
定义枚举类型时也允许省略数据类型名称,将变量声明为特定枚举数据类型中的一个。如:
/*定义未命名枚举数据类型(包含值为east、west、south和north),同时声明该类型的变量direction*/ enum {east, west, south, north} direction;
typedef语句
使用typedef语句可为数据类型另外指派一个名称。
typedef int Counter; //定义名称Counter等价于数据类型int,随后变量可声明为Counter类型 Counter j, n; //编译器实际上将变量j和n的声明当做普通整型int变量声明 typedef Number *NumberObject; //定义名为NumberObject的类型,是Number对象 NumberObject myValue1, myValue2, myResult; //等价于 Number *myValue1, *myValue2, *myResult; typedef enum {east, west, south, north} Direction; //定义名为Direction的枚举数据类型 Direction step1, step2; //声明Direction类型的变量
数据类型转换
表达式求值过程中不同类型操作数发生转换的先后顺序(简化):
1. 如果其中一个操作数是long double型,则另一操作数被转换为long double型,计算结果也是。
2. 如果其中一个操作数是double型,则另一操作数被转换为double型,计算结果也是。
3. 如果其中一个操作数是float型,则另一操作数被转换为float型,计算结果也是。
4. 如果其中一个操作数是Bool、char、short int、bit field或枚举数据类型,则全部转换为int型。
5. 如果其中一个操作数是long long int型,则另一操作数被转换为long long int型,计算结果也是。
6. 如果其中一个操作数是long int型,则另一操作数转换为long int型,计算结果也是。
7. 如果到达这一步,则可知两个操作数均为int型,计算结果也是。
位运算符
位运算符除一次求反运算符(~)外都是二元运算符。
位运算符可处理任何类型的整型值,但不能处理浮点值。
按位与运算符&的优先级高于按位或运算符|。