- 类与对象
- 数据成员
- 函数成员
TODO:
- 初始化器和析构器
- 继承
- 多态
类与对象
Objective-C(以下简称OC) 是一种面向对象语言,因此除了基本数据类型,它具有类类型。OC 中类的声明形式类似:
@interface MyClass: NSObject
{
NSString* private; //实例变量
@public NSString* public; //实例变量
}
@property int num; //属性
-(void)ObjectMethod: (int)num; //对象方法,又叫实例方法
+(void)ClassMethod; //类方法
-(NSString*)private ;
-(void)setPrivate: (NSString*)str;
@end
OC 用 @interface 声明类,相当于C++语法中的class关键字。@end表示声明的结束。OC中的大部分中大部分类都会直接或者间接继承自NSObject,这是因为NSObject中定义了一些OC对象通用的方法,比如定义一个类时[[MyClass alloc] init],其中alloc和init方法就是在NSObject中定义的,否则我们就要在类定义中自己实现这两个方法。
OC中类的声明放在.h文件,类是现实存在.m文件。上面声明的类其实现类似:
@implement MyClass
-(void)ObjectMethod: (int)num
{
self.num = num;
}
+(void)ClassMethod
{
NSLog(@"This ClassMethod");
}
-(NSString*)private {
return private;
}
-(void)setPrivate: (NSString*)str {
private = str
}
定义一个类对象的语法如下:
MyClass* myClass = [[MyClass alloc] init]
大概是因为OC中的类对象都是分配在堆上的,所以,类对象都声明为指针的形式。
数据成员:属性和实例变量
上述类声明中的有一个数据成员,就是
@property in num;
属性的存取方式
myClass.num = 5;
int someNum = myClass.num;
对于任何属性,编译器都会自动生成一个与之关联的实例变量。比如与num关联的实例变量是_num。同时还生成一个getter访问器方法和一个setter访问器方法。
-(int)num {
return _num;
}
-(void)setNum: (int)num {
_num = num;
}
可以发现这里的_num和它的两个访问器和我们在MyClass实现中定义的NSString* private;形式是类似的。
private和public一样是实例变量,不同的是,一般的实例变量(如:private)只能在类的内部访问,都是私有的。为了能在类的外部访问(如myClass->public = @"hello";),需要在实例变量声明语句加上@public前缀(如:public)。
虽然private不能在外部通过->来访问,但因为我们为private编写了getter访问器和setter访问器,在OC中,我们就可以使用myClass.private = @"world"这样的语法来访问private,其实编译器会自动将该语句转化成[myClass setPrivate:@"world"],也就是实际是调用setter方法来访问private实例变量的。
可想而知,如果没有属性,那么对于每个我们希望通过myClass.XXX形式来访问的实例变量,我们都需要为其编写两个访问器方法,这实在是太过繁琐。有了属性这个语法糖,就可以让编译器自动为我们完成这些工作了。
同样,有了属性,我们也不需要再使用@public关键字,通过->指针访问符来访问实例变量了。
需要注意的时,对于在.h和.m文件中分别编写类声明和类实现代码情况,即我们通常所遵循的方式。外部使用时通过#import "xxx.h"只能知道xxx.h文件中的定义,如果我们将某个实例变量定义在xxx.m的实现代码中,那么就是为该实例变量加上@public前缀,外部对象也不能访问该实例变量,因为它根本不知道该实例变量的存在。这点对于方法也是成立的,尽管方法都是public的,但如果方法只在.m文件中定义,没有在.h文件中声明,那么对于外部对象该方法就相当于是私有的。相当于私有并不等于私有,这点在编译器报错时可以体现。只定义在.m文件中情况,访问时编译器会提示没有定义该实例变量或者方法,而如果是声明在.h文件中非@public实例变量,访问时编译器会提示该实例变量是受保护的。
函数成员:方法
OC中的方法都是public的,没有 private 和 protected 方法。
OC中类的方法分为实例方法和类方法,声明形式如下
-(void)ObjectMethod: (int)num; //对象方法,又叫实例方法
+(void)ClassMethod; //类方法
在语法层面,实例方法就是通过实例对象来调用的方法,如[myClass ObjectMethod:10],定义时在方法名前加-前缀;类方法就是通过类名来调用的方法,如[MyClass ClassMethod],定义时在方法名前加+前缀。
[[MyClass alloc] init],其中alloc就是类方法,init就是实例方法。
类方法中是不能访问本类的实例成员的。在逻辑上类方法可以在类对象不存在时调用,此时实例变量都还不存在,那么通过类方法访问实例成员必然出错。即使类对象存在,假设有多个类对象,那么类方法是访问那个类对象的实例成员也是无法确定。在实现上,编译器会为实例方法自动添加指向当前对象的self指针参数,通过self就能确定所访问的对象。而类方法是没有self参数的,所以类方法无法确定是哪个类对象。
OC中方法的外部参数名不同,就是不同的方法。因此可以写方法名相同,参数类型和数量相同,以及返回值相同的,只有外部参数名不同的多个方法。
-(void)sum: (int)arg1 arg2: (int)arg2;
-(void)sum:(int)arg1 secondArg: (int)arg2;
这样的两个方法是可以同时存在的。
初始化器和析构器
初始化器
MyClass* myClass = [[MyClass alloc] init]
OC中创建一个对象需要配合使用alloc和init两个方法。正如方法名的字面意思,alloc方法用于分配对象空间,init方法用于对对象的实例变量进行初始化操作。
alloc方法在NSObject中已经定义,这也是我们的类要继承于NSObject的原因之一。alloc方法会将分配的内存空间用0来填充,这样所有的实例变量的值就会是0或者nil。
在init的方法中,会先调用父类的init方法,再初始化自己的实例变量。
init是对象初始化器,在调用init之前还会调用一个类初始化器initialize。
@implementation MyClass
...
-(id)init {
self = [super init];
NSLog(@"this init");
return self;
}
+(void)initialize {
if self == [MyClass class] {
NSLog(@"this is initialize");
}
}
...
@end
在类定义中实现这两个初始化器,然后调用MyClass* myClass = [[MyClass alloc] init]
会输出如下结果:

类型初始化器initialize只能有一个,而对象初始化器init可以有多个。
- (id)init;
- (id)initWithName: (NSString*)name;
- (id)initWithLocation: (NSPoint*)location;
在初始化器中,要使用实例变量,不要使用属性。
析构器 dealloc
在析构器中,主要做三件事
- ARC对对象属性的引用计数器减1,这个造作是自动完成。
- 手动释放我们自己分配的动态内存
- 关闭非内存资源,比如文件句柄,socket连接等