Objective-C(02)|类的定义和对象初始化(初阶)
书接前文,奇怪的语法体验继续!
对象的初始化
和Java的new运算符不同,OC的对象初始化使用初始化方法(initializer),个人觉得比new运算符更酷,这方法名和C语言中动态分配内存空间的malloc很像。
实例的生成:
[ClassName alloc]
Cocoa中某个类的对象的生成:
[[ClassName alloc] init ]
通常嵌套调用alloc和init来生成对象。
初始化方法并不具备该对象的重置功能,需单独使用reset方法。
类的定义
和Java类似,OC中类的定义和实现可以分离,即类本身和对外的接口(我是这么理解的)。
接口声明
类的接口部分定义类的实例变量和方法,通常声明为头文件。给需要调用这个类的模块引用。
接口的声明eg:
@interface ClassName : SuperClassName
{
id value1;
int value2;
double value3;
BOOL value4;
}
- (id)method1: (id)obj;
- (void)dealloc;
- (double)method2: (int)someone;
@end
所有的OC编译指令(compiler directive)都以“@”开头,和C语言的字符串区分。类的接口声明使用@interface和@end包起来,@end后不接分号。
接口声明,必须写父类。
类的实现
类的实现部分不用再次声明父类。
实现部分则是上述接口声明中所有方法的实现。方法内部可以自由使用实例变量。方法内部定义的局部变量和C语言的局部变量同理。若局部变量和实例变量重名,则实例变量将被覆盖,方法的参数名同理。方法中的“self”即实例本身,相当于Java的“this”。
OC的源代码文件以“.m”结尾,m意味模块,使用clang编译器即可识别为Objective-C源程序文件。
接口的声明必须放在实现和main函数之前。
一个例子
书中的例子,代码:
接口定义为头文件:Volume.h
#import <Foundation/NSObject.h>
@interface Volume : NSObject
{
int val;
int min, max, step;
}
- (id)initWithMin:(int)a max:(int)b step:(int)s;
- (int)value;
- (id)up;
- (id)down;
@end
类的实现:Volume.m
#import "Volume.h"
@implementation Volume
- (id)initWithMin:(int)a max:(int)b step:(int)s
{
self = [super init];
if (self != nil)
{
val = min = a;
max = b;
step = s;
}
return self;
}
- (int)value
{
return val;
}
-(id)up
{
if ((val += step) > max)
val = max;
return self;
}
-(id)down
{
if ((val -= step) < min)
val = min;
return self;
}
@end
可执行程序入口,main函数:VolumeTest.m
#import <stdio.h>
#import "Volume.h"
int main(void)
{
id v, w;
v = [[Volume alloc] initWithMin: 0 max:10 step:2];
w = [[Volume alloc] initWithMin: 0 max:9 step:3];
[v up];
printf("%d %d\n", [v value], [w value]);
[v up];
[w up];
printf("%d %d\n", [v value], [w value]);
[v down];
[w down];
printf("%d %d\n", [v value], [w value]);
return 0;
}
类的实现.m文件,必须使用import导入接口的头文件,而头文件中若不指定具体的继承类,则强制继承NSObject类。
体会和理解
奇怪的语法,从直接感受来说,接口的定义,或者说方法的定义,在参数的表示部分很像消息表达式,例如本例中的
- (id)initWithMin:(int)a max:(int)b step:(int)s
如果用C或者Java的眼光来看,参数列表不用小括号包裹就已经很不习惯了,但是这里的参数列表貌似又显得更有“人情味”;同消息表达式中:
通常把消息的关键字按照英文的语序来组织。
那么此处,参数a就是给初始化最小值用,b给实例参数max用,s给实例参数step用,这样的话,尤其是第一个参数就有了用处的表达,表达内容就是方法名。
这样的定义方式,,习惯和熟练运用需要时间。
指定初始化方法(designed initializer)
指能确保所有实例变量都能够完成初始化的方法,这种方法是初始化的核心,类的非初始化方法会调用指定初始化方法完成初始化。
一般地,接收参数最多的初始化方法就是指定初始化方法,一个类可以有不止一个的指定初始化方法。
子类的指定初始化方法通常是给super发送消息来调用超类的指定初始化方法,且必须调用超类的指定初始化方法。
继承关系中,各个类的指定初始化方法会从下到上连锁调用,直到最上层的NSObject的init方法为止,故需注意不要造成递归无限循环。
还有一些通过封装来调用指定初始化方法的非指定初始化方法。