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方法为止,故需注意不要造成递归无限循环。
还有一些通过封装来调用指定初始化方法的非指定初始化方法。

posted @ 2023-12-27 11:03  Ludwig_1122  阅读(4)  评论(0编辑  收藏  举报