<7>类的本质
1. 类本身也是对象,是个Class类型的对象,简称类对象;而类(类对象)所创建的对象叫做实例对象,它所属的类型是创建它的那个类(注意的是:Class本身带有 * )
2. 内存中每个类只加载一次(一份)
Class c = [p class]; // p为Person类的一个对象 此时c是Person类的地址(返回一个类的地址,所以c也可以创建一个Person类的对象),这叫做获取内存中的类对象
Class c1 = [p1 class]; // p1为Person类的一个对象 此时c1是Person类的地址(返回一个类的地址),这叫做获取内存中的类对象
所以 c = c1
获取内存中类的另一种方法
Class c2 = [Person class]; //c=c1=c2
输出地址用%p
3. 类对象就是类就是类名
4. + (void)load方法:当程序启动的时候,就会加载一次项目中所有的类和分类,类加载完毕后就会调用+load方法,每个类调用一遍但是只会调用一次(不管你用没用到这个类都会调用这个方法)
+ (void)initialize方法:当第一次使用这个类的时候,就会调用+initialize方法(只有用到这个类,才会调用这个方法)
先加载父类,再加载子类(先调用父类的+load方法,在调用子类的+load方法)
先初始化父类,再初始化子类(先调用父类的+initialize方法,在调用子类的+initialize方法)(利用这个方法可以监听这个类在什么时候被使用了,也可以通过重写该类的init方法来让这个类在第一次使用时初始化我们想做的一些事)
<8>description方法
1. 打印对象用%@
2. 打印对象中的所有属性值(描述这个对象)
3. -description方法(不在NSObject类中):
Person *p = [[Person alloc] init];
NSLog(@“%@”, p);
默认情况下,用NSLog和%@‘直接’输出对象时,结果是:<类名:内存地址>:实际上分为下面的三步
1>当使用NSLog函数和%@输出对象时,会首先调用对象p的-description方法(-description方法的返回值是NSString *)
2>拿到-description方法的返回值(NSString *)显示到屏幕上
3>-description方法默认返回的是“类名+内存地址”
可以通过重写-description这个方法来打印p对象
- (NSString *)description
{
return [NSString stringWithFormat:@“age=%d,name=%@”, _age, _name]; //返回一个由对象属性值组成的字符串
}
这时再NSLog(@“%@”, p); 就是p对象的属性值了(描述p)
还有要注意避免引发死循环!(不要在重写的-description方法中输出一个self或者说一个对象)
-description方法决定了实例对象的输出结果
4. +description方法(在NSObject类中)
Class c = [Person class];
NSLog(@“%@”, c);
1>会调用类的+description方法
2>拿到+description方法的返回值(NSString *)显示到屏幕上(结果:该类的类名)
同样可以重写+description方法来打印类对象
+description方法决定了类对象的输出结果
5. 输出补充
NSLog(@“%p”, p); = NSLog(@“%@”, p); //都是p的地址,不重写-description方法的前提下
查看NSLog的文档 _LINE_ _FILE_ _func_
<9>SEL类型
1. SEL类型的变量里存的是类的方法的地址
1>把方法包装成SEL类型的数据
2>根据SEL数据找到对应的方法地址
3>根据方法地址调用对应的方法
4>第一次包装的时候是比较耗性能的,他得一个一个找/匹配,但是做完后就会有一个缓存,以后就不用做了,直接查找缓存
2. 调用方法的两种方式(直接的 间接的)
不带参数的
直接:[p test];
间接:[p performSelector:@selector(test)];
带参数的
[p test3:@“abc”];
[p performSelector:@selector(test3:) withObject:@“abc”]; // : 也是方法名的一部分
发消息就是发SEL数据(消息机制)
3. SEL本身也有 *
4. 每个类的方法列表都存储在类对象中,每个方法都有一个与之对应的SEL数据对象,根据一个SEL对象就可以找到方法的地址,进而调用该方法
5. SEL对象的创建
1> SEL s1 = @selector(test:); // 这里是带参数的方法test,所以有冒号,: 也是方法名的一部分
2> 知道类中有一个方法名叫‘test’ ,但是这个方法名是一个字符串,我们知道一个方法名的字符串形式,怎么调用该方法?
SEL s2 = NSSelectorFromString(@“test”); // 1.先把字符串形式的方法名转换成SEL数据
[p performSelector:s2]; // 2.再调用该方法的SEL数据(传给performSelector方法)
例子:
Person *p = [[Person alloc] init];
NSString *name = @“test”;
SEL s2 = NSSelectorFromString(name);
[p performSelector:s2];
6. 每个方法中都有一个隐藏的 _cmd SEL数据,它指向当前的方法(指向本身),要想输出 _cmd,要先把_cmd转换成字符串,然后再输出,不能直接输出(有相应的方法),不能在方法中用_cmd, 否则会引发死循环
7. 学到运行机制时,就可以使用这个SEL直接拿到方法地址调用方法,如果一个方法使用非常频繁,包装比较麻烦,会耗费性能,所以我们可以直接拿到方法的SEL去调用方法,降低程序运行的消耗,提高性能
相关文章
- 阶段1 语言基础+高级_1-3-Java语言高级_02-继承与多态_第2节 抽象类_16-抽象方法和抽象类的使用
- 【java基础 16】抽象类和接口的区别
- Java基础知识点(类的几个补充注意事项和private关键字)
- 黑马程序员——OC面向对象的基础认识
- 黑马程序员——OC的初步认识,类和对象
- 黑马程序员-OC的类和对象的初步认识
- 黑马程序员——OC的初步认识,类和对象
- 黑马程序员——OC基础---面向对象(思想,类,对象,三大特性)
- JAVA基础 day13 String类和StringBUffer类的常用方法 基本数据类型包装类的学习
- 黑马程序员——OC基础学习(二)---对象方法和类方法的学习知识总结