1、如何描述一个类:
类名称:类名第一个字母用大写,虽然也能用中文,但是不建议;
属性(成员变量):属性写在@interface与@end之间的{ }内,而且按照惯例属性以_开头;
行为(成员方法):方法类似与C语言中的函数,声明写在@interface { }之外,@end以内。
//类的声明
@interface People :NSObject{ //声明People类,继承自NSObject类
int _name; //姓名
int _age; //年龄
}
-(void)about; //对象方法,用-开头,只有实例化一个该类的对象,通过该对象来调用
+(void)info; //类方法,以+开头,通过类名来调用
@end
//类的实现
@implementation People
//对象方法
-(void)about {
NSLog("about方法");
NSLog("姓名:%d,年龄:%d",_name,_age);
}
//类方法
+(void)info {
NSLog("info方法");
NSLog("姓名:%d,年龄:%d",_name,_age);
}
@end
注意:类可以只有实现,没有声明。
2、通过一个类来创建一个对象:
//方法一:
People *p1 = [People new]; //由于继承自NSObject,所以可以使用NSObject的new方法
//方法二:
People *p2 = [[People alloc] init];
系统创建一个对象所做的事:
a、为People类创建出来的对象分配存储空间;
b、初始化People类创建出来的对象中的属性;
c、返回People类创建对来的对象对应的地址。
3、OC中的方法:
类方法:通过类名来调用,如:[People info];
对象方法:只能通过实例化一个对象,通过该对象来调用,如:
[[People new] about];
区别:
a、对象方法必须使用对象来调用,类方法必须通过类来调用;
b、对象方法中可以直接访问属性(成员变量),而类方法不可以;
c、类方法的调用效率比对象方法高。
补充:对象方法和类方法可以互相调用
类方法的使用场景:
a、如果方法中没有使用到属性(成员变量),那么就可以将该方法定义成类方法;
b、类方法的执行效率比对象方法高;
c、类方法一般用于定义工具方法:字符串查找,文件操作,数据库操作。
4、对象在内存中的存储细节:
Person{ //Person类
int _age;
double _weight;
}
-(void)about;
Person *p = [Person new];
创建Person类的对象:
a、开辟存储空间,通过new方法创建对象会在堆内存中开辟一块存储空间;
b、初始化所有属性;
c、返回对象的指针。
**
解析:
创建对象时返回的地址其实就是类的第0个属性的地址,但是第0个属性并不是上面所示的_age属性,而是一个叫isa的属性,isa是一个指针,占8字节,类也是一个对象,也就意味着上面的Person也是一个对象,平时我们所说的创建对象,其实就是通过一个类对象来创建一个新的对象,类对象是系统自动帮我们创建的,里面保存了该类所有的方法,而实例对象是我们通过new来手动创建的,实例对象中有一个isa指针,就是指向创建它的那个类对象。
**
总结:类创建对象时,每个对象在内存中占据一定的存储空间,每个对象都有一份属于自己的单独的成员变量,所有对象公用类的成员方法,方法在整个内存中只有一份,类本身在内存中占据一份存储空间,类的方法存储于此。
5、成员变量,局部变量,全局变量
(1)、成员变量:写在类声明大括号中的变量我们称之为成员变量(属性,实例变量)
特性:成员变量只能通过对象来访问,成员变量不能离开类,成员变量不能再定义时进行初始化
存储位置:堆(存储在堆中的数据,不会被自动释放,只能由程序员手动释放)存在于当前对象对应的堆存储空间中
(2)、局部变量:写在函数或者代码块中的变量我们称之为局部变量
作用域:从定义的那行开始到遇到大括号或者return为止
特性:局部变量可以先定义再初始化,也可以边定义边初始化
存储位置:栈(存储在栈中,系统会自动为我们释放)
(3)、全局变量:写在函数或者大括号外面的变量,我们称之为全局变量
作用域:从定义那行开始,到文件结尾
存储:静态区(程序一启动就会分配空间,知道程序结束才会释放)
6、函数与方法
(1)、函数属于整个文件,方法属于某个类,方法不能离开类
(2)、函数可以直接调用,而方法需要用类或者对象来调用
(3)、函数不能写在类的声明中
(4)、函数和方法不能按对方的方式来调用
(5)、方法可以只有声明没有实现但是运行会报错,也可以只有实现没有声明
7、self关键字
(1)、self不但可以调用类方法,也可以调用对象方法,此时self==对象;
(2)、如果self在对象方法中,那么self就代表当前调用当前对象方法的那个对象;如果self在类方法中,那么self就代表当前的那个类;
(3)、self会自动区分类方法和对象方法,如果self在类方法中直接调用对象方法,那么会直接报错,同理,如果在对象方法中直接调用类方法,也会直接报错;
(4)、不管self是在类方法中还是在对象方法中,都不能直接用self调用自己方法本身,如:
-(void)test {
[self test]; //直接造成死循环
}
+(void)test {
[self test];
}
(5)、self可以在对象方法中直接相互调用对象方法,而不用实例化一个对象,同理,self也可以在类方法中直接代用类方法,此时self==当前类,如:
-(void)test {
[self info]; //相当与Class *c = [Class new]; [c info];省去实例化的过程
}
-(void)info {
NSLog(@"此处不能调用test方法,不然会造成死循环");
}
8、super关键字
super关键字与self关键字相似,但是self代表自己,super代表父类,super适用于继承关系。
9、多态
多态:事物的多种形态
在程序中的变现形式:父类指针指向子类对象
PS:如果父类指针指向子类对象,但此时需要调用子类特有的方法,必须先强制转换成子类的类型再调用。
例子1:
Animal:NSObject
-(void)eat {
NSLog(@"吃东西");
}
Dog:Animal
-(void)eat {
NSLog(@"啃骨头");
}
-(void)lookDoor {
NSLog(@"看门");
}
Animal *a = [Dog new]; //这就是多态
[a eat]; /*此时会做两件事
在编译时检查Animal中是否有eat方法,如果没有,将会报错;
在运行时,自动判断a的真实类型(动态类型),将a的类型设置成Dog。所以此时打印的是-->啃骨头*/
//[a lookDoor]; //报错,Animal中没有lookDoor方法
Dog *d = (Dog *)a; //强制类型转换
[d lookDoor]; //正确,打印-->看门
例子2:
Animal:NSObject
-(void)eat{
NSLog[@"吃东西"];
}
Person:Animal
/*
+(void)food:(Dog *)d{
[d eat];
}
+(void)food:(Cat *)c{
[c eat];
}
*/
//直接使用下面这个方法替换,但是具有上面相同的功效
+(void)food:(Animal *)a{
[a eat];
}
Dog:Animal
-(void)eat{
NSLog[@"啃骨头"];
}
Cat:Animal
-(void)eat{
NSLog[@"吃鱼"];
}
Dog *dog = [Dog new];
Cat *cat = [Cat new];
[Person food:dog]; //打印啃骨头
[Person food:cat]; //打印吃鱼
/*
好处:不用每增加一种动物,Person类都增加一种喂食方式
*/