这里是很基本的知识点,其中提到的实际应用可以参考如下
AOP Runtime应用
方法介绍
Hook函数之无码埋点
Class 是一个 objc_class 结构类型的指针, id是一个 objc_object 结构类型的指针.
struct objc_class {
struct objc_class *isa;
};
struct objc_object {
struct objc_class *isa;
};
typedef struct objc_class *Class; //类 (class object)
typedef struct objc_object *id; //对象 (instance of class)
在objc中,id代表了一个对象。根据上面的声明,凡是首地址是*isa的struct指针,都可以被认为是objc中的对象。运行时可以通过isa指针,查找到该对象是属于什么类(Class)。
@interface Dog : NSObject{
NSInteger _age;
NSString *name;
}
- (void)pengta;
+ (void)hehe;
@end
@implementation Dog
- (void)pengta{
printf("instance dog");
}
+ (void)hehe{
printf("class dog");
}
@end
clang -rewrite-objc Dog.m 编译成C++语言
//Class包含的信息
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
unsigned int reserved;
const unsigned char *ivarLayout;
const char *name; // 类名
const struct _method_list_t *baseMethods; // 方法列表
const struct _objc_protocol_list *baseProtocols; // 协议列表
const struct _ivar_list_t *ivars; // 变量列表
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties; // 属性列表
};
//Class的实际结构
struct _class_t {
struct _class_t *isa; // isa指针
struct _class_t *superclass; // 父类
void *cache;
void *vtable;
struct _class_ro_t *ro; // 具体信息就是上面个结构体属性
};
// Dog (Meta-Class)
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Dog __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_Dog, // 指向下面的结构体 包含了类方法
};
// Meta-Class里面的属性,包含类方法
static struct _class_ro_t _OBJC_METACLASS_RO_$_Dog __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1, sizeof(struct _class_t), sizeof(struct _class_t),
(unsigned int)0,
0,
"Dog",
(const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_Dog, // 类方法
0,
0,
0,
0,
};
// Dog (Class )
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Dog __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_Dog, // 这里的isa指向meta-Class
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_Dog, // 指向下面的结构体 包含了实例方法 变量的信息
};
// Class里面的属性,包含实例方法和变量信息
static struct _class_ro_t _OBJC_CLASS_RO_$_Dog __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, __OFFSETOFIVAR__(struct Dog, _age), sizeof(struct Dog_IMPL),
(unsigned int)0,
0,
"Dog",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Dog,
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_Dog,
0,
0,
};
typedef struct objc_object Dog; // 定义的Dog类型
所有Dog的实例的isa都指向了Dog(Class)。
Dog(Class)是一个全局变量,其中记录了类名、成员变量信息、property信息、protocol信息和实例方法列表等。
Dog(Class)的isa指向了全局变量Dog(meta-class),meta-class里只记录了类名、类方法列表等。
Dog *dog = [[Dog alloc] init];
[dog pengta];
向Dog (instance) 发送消息pengta时,运行时会通过isa指针查找到Dog(Class),这里保存着本类中定义的实例方法的指针。
[Dog hehe];
向Dog(Class)发送消息hehe时,运行时会通过isa查找到Dog(meta-class),这里保存着本类中定义的类方法的指针。
引用自掘金的介绍
所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。如:
NSArray *array = [NSArray array];
这个例子中,+array消息发送给了NSArray类,而这个NSArray也是一个对象。既然是对象,那么它也是一个objc_object指针,它包含一个指向其类的一个isa指针,这个isa指针也要指向这个类所属的类。那么这些就有一个问题了,这个isa指针指向什么呢?这就引出了meta-class的概念:
meta-class是一个类对象的类
当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。
meta-class 是必须的,因为它为一个 Class 存储类方法。每个Class都必须有一个唯一的 meta-class,因为每个Class的类方法基本不可能完全相同。
再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类 (在一定程度上可以理解为若一个Class继承自NSObject,则这个Class的meta-class继承自NSObject的meta-class),而基类的meta-class的isa指针是指向它自己,这就是说 NSObject 的 meta-class 的 isa 指针指向NSObject 的 meta-class自己。这样就形成了一个完美的闭环。
这个图一般讲到isa或者Runtime的时候都会贴出来,这里根据流程介绍下
如上面图中,跟随虚线,可以看到isa的指向。运行时,每个对象的isa都不为空,这样只要是一个id类型的对象,runtime都可以通过访问首地址偏移(isa)来获取该对象的信息了。
上图中跟随实线线,可以看到superclass的指向。当运行时在搜寻方法、ivar信息时,如果没有找到信息,则会沿superclass的线查找上去,最终NSObject(根类)的superclass是nil。
1.也就是编译期间会建立SEL和IMP的结构 (编译阶段)
2.然后运行期间先横向查找,再纵向查找(消息分发)
3.最后进入调用进入_objc_msgForward (消息转发阶段 几个方法进行拦截)
如果自己定义了一个根类(比如NSProxy),则这个根类会替换图中NSObject的位置
Cat *cat = [Cat new];
// 2018-03-30 16:39:26.519352+0800 RuntimeC[6916:370297] Cat,0,Cat
Class cls = object_getClass(cat); // Cat(class)
const char *A = class_getName(cls); // "Cat"
BOOL B = class_isMetaClass(cls); // NO
NSLog(@"%s,%@,%@",A,@(B),cls);
// 2018-03-30 16:39:26.519473+0800 RuntimeC[6916:370297] Cat,1,Cat
Class cls1 = object_getClass(cls); // Cat(meta - class)
const char *A1 = class_getName(cls1); // “Cat”
BOOL B1 = class_isMetaClass(cls1); // YES
NSLog(@"%s,%@,%@",A1,@(B1),cls1);
// 2018-03-30 16:39:26.519585+0800 RuntimeC[6916:370297] NSObject,1,NSObject
Class cls2 = object_getClass(cls1); // NSObject(meta - class)
const char *A2 = class_getName(cls2); // "NSObject"
BOOL B2 = class_isMetaClass(cls2); // YES
NSLog(@"%s,%@,%@",A2,@(B2),cls2);
实例对象测试
//obj为实例变量
id obj = [TestObject new];
Class cls = object_getClass(obj);
Class cls2 = [obj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
2015-12-17 14:08:32.196 TestProject[658:29503] 0x100001140
2015-12-17 14:08:32.197 TestProject[658:29503] 0x100001140
结论:当obj为实例变量时,object_getClass(obj)与[obj class]输出结果一直,均获得isa指针,即指向类对象的指针。
//obj为实例变量
id obj = [TestObject new];
//classObj为类对象
Class classObj = [obj class];
Class cls = object_getClass(classObj);
Class cls2 = [classObj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
2015-12-17 14:25:48.785 TestProject[813:38336] 0x100001118
2015-12-17 14:25:48.786 TestProject[813:38336] 0x100001140
虽然打印的名字是一样的,但是地址是不同的cls指向的是元类,cls2是自身
结论:当obj为类对象时,object_getClass(obj)返回类对象中的isa指针,即指向元类对象的指针;[obj class]返回的则是其本身。元类测试
//obj为实例变量
id obj = [TestObject new];
//classObj为类对象
Class classObj = [obj class];
//metaClassObj为元类对象
Class metaClassObj = object_getClass(classObj);
Class cls = object_getClass(metaClassObj);
Class cls2 = [metaClassObj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
2015-12-17 14:41:23.386 TestProject[921:47694] 0x7fff78fb9118
2015-12-17 14:41:23.387 TestProject[921:47694] 0x100001118
结论:当obj为Metaclass(元类)对象时,object_getClass(obj)返回元类对象中的isa指针,因为元类对象的isa指针指向根类,所有返回的是根类对象的地址指针;[obj class]返回的则是其本身。
根类测试
//obj为实例变量
id obj = [TestObject new];
//classObj为类对象
Class classObj = [obj class];
//metaClassObj为元类对象
Class metaClassObj = object_getClass(classObj);
//rootClassObj为元类对象
Class rootClassObj = object_getClass(metaClassObj);
Class cls = object_getClass(rootClassObj);
Class cls2 = [rootClassObj class];
NSLog(@"%p" , cls);
NSLog(@"%p" , cls2);
2015-12-17 14:52:34.633 TestProject[965:54693] 0x7fff78fb9118
2015-12-17 14:52:34.634 TestProject[965:54693] 0x7fff78fb9118
参考
点击打开链接
对象和类