标签:
类的本质:
获取类对象:
通过实例对象:
格式:[实例对象 class ];
如: [dog class];
通过类名获取(类名其实就是类对象)
格式:[类名
class];
如:[Dog class]
类对象的用法:
用来调用类方法:
[Dog test];
Class
c = [Dog class];
[c test];
用来创建实例对象:
Dog *g = [Dog new];
Class
c = [Dog class];
Dog *g1 = [c new];
类对象的存储:
OC实例对象 类对象 元对象:
OC对象:
1 每一个对象都是一个类的实例。
2 每一个对象都有一个名为isa的指针,指向该对象的类。
3 每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。
4 每一个类实际上也是一个对象。每一个类也有一个名为isa的指针。每一个类都可以接受消息,例如[NSObject new],就是向NSObject这个类发送名为new的消息。
元类:
因为类也是一个对象,那它也必须是另一个类的实例,这个类就是元类 (metaclass)
1 元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有则该元类会向它的父类查找该方法,直到一直找到继承链的头。
2 元类(metaclass)也是一个对象,也有isa指针,所有的元类的isa指针都会指向一个根元类(root metaclass)。
3 根元类(root metaclass)本身的isa指针指向自己,这样就行成了一个闭环。一个对象能够接收的消息列表是保存在它所对应的类中的。在实际编程 中,我们几乎不会遇到向元类发消息的情况,那它的isa 指针在实际上很少用到。不过这么设计保证了面向对象的干净,即所有事物都是对象,都有isa指针。
4 由于类方法的定义是保存在元类(metaclass)中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。
假设: Person类有一个对象方法test 和 一个类方法 demo
Student类有一个对象方法test1 和一个类方法 demo1
在实现中,Root Class是指 NSObject
NSObject类对象包括它的对象实例方法。
NSObject的元对象包括它的类方法,例如new方法。
NSObject的元对象继承自NSObject类。
一个NSObject的类中的方法同时也会被NSObject的子类在查找方法时找到。
类的启动过程:
+load方法:
1 在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法(只会调用一次),将所有类的代码加载到内存中, 放到代码区
2 先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load
3 先加载原始类,再加载分类
4 不管程序运行过程有没有用到这个类,都会调用+load加载
@implementation Person + (void)load { NSLog(@"%s", __func__); } @end @implementation Student : Person + (void)load { NSLog(@"%s", __func__); } @end 输出结果: +[Person load] +[Student load]
+initialize方法:
1 在第一次使用某个类时(比如创建对象等),会调用一次+initialize方法, 无论使用多少次这个类都只会调用一次
2 initialize用于对某一个类进行一次性的初始化
3 一个类只会调用一次+initialize方法,先调用父类的,再调用子类的
@implementation Person + (void)initialize { NSLog(@"%s", __func__); } @end @implementation Student : Person + (void)initialize { NSLog(@"%s", __func__); } @end int main(int argc, const char * argv[]) { Student *stu = [Student new]; return 0; } 输出结果: +[Person initialize] +[Student initialize]
SEL类型:
概念:
1 SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系
2 每个类的方法列表都存储在类对象中, 每个方法都有一个与之对应的SEL类型的对象, 根据一个SEL对象就可以找到方法的地址,进而调用方法
3 SEL类型的定义 typedef struct objc_selector *SEL;
-test方法的调用:
1 首先把test这个方法名包装成sel类型的数据
2 根据SEL数据到该类的类对象中,去找对应的方法的代码,如果找到了就执行该代码
3 如果没有找到根据类对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码
4 如果没有找到,一直像上找,直到基类(NSObject), 如果都没有找到就报错。
在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。
用途:
1 配合对象/类来检查对象/类中有没有实现某一个方法
SEL sel = @selector(setAge:); Person *p = [Person new]; // 判断p对象中有没有实现-号开头的setAge:方法 // 如果P对象实现了setAge:方法那么就会返回YES // 如果P对象没有实现setAge:方法那么就会返回NO BOOL flag = [p respondsToSelector:sel]; NSLog(@"flag = %i", flag); // respondsToSelector注意点: 如果是通过一个对象来调用该方法那么会判断该对象有没有实现-号开头的方法 SEL sel1 = @selector(test); flag = [p respondsToSelector:sel1]; NSLog(@"flag = %i", flag); // 如果是通过类来调用该方法, 那么会判断该类有没有实现+号开头的方法 flag = [Person respondsToSelector:sel1]; NSLog(@"flag = %i", flag);
2 配合对象/类来调用某一个SEL方法
SEL sel = @selector(test); Person *p = [Person new]; // 调用p对象中sel类型对应的方法 [p performSelector:sel]; // withObject: 需要传递的参数 // 注意: 如果通过performSelector调用有参数的方法, 那么参数必须是对象类型,因为withObject只能传递一个对象 SEL sel2 = @selector(setAge:); [p performSelector:sel2 withObject:@(5)]; NSLog(@"age = %i", p.age); // 注意:performSelector最多只能传递2个参数 SEL sel3 = @selector(sendMessageWithNumber:andContent:); [p performSelector:sel3 withObject:@"138383438" withObject:@"abcdefg"];
3 配合对象将SEL类型作为方法的形参
// 调用传入对象的指定方法 - (void)makeObject:(id)obj andSel:(SEL)sel; - (void)makeObject:(id)obj andSel:(SEL)sel { [obj performSelector:sel]; } Car *c = [Car new]; SEL sel = @selector(run); Person *p = [Person new]; [p makeObject:c andSel:sel];
标签:
原文地址:http://www.cnblogs.com/dx-230/p/4787600.html