Objective-C中的类和对象(isa介绍)

前言

这里是很基本的知识点,其中提到的实际应用可以参考如下

AOP Runtime应用

方法介绍

Hook函数之无码埋点

在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针。

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)。

是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是: 元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass).根元类的isa指针指向本身,这样形成了一个封闭的内循环。


介绍

@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),这里保存着本类中定义的类方法的指针。


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自己。这样就形成了一个完美的闭环。



Objective-C中的类和对象(isa介绍)_第1张图片

这个图一般讲到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);

object_getClass(obj)与[obj class]的区别


实例对象测试

//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
结论:当obj为Rootclass(元类)对象时,object_getClass(obj)返回根类对象中的isa指针,因为跟类对象的isa指针指向Rootclass‘s metaclass(根元类),即返回的是根元类的地址指针;[obj class]返回的则是其本身。
因为根类的isa指针其实是指向本身的,所有根元类其实就是根类,所有输出的结果是一样的。

总结:经上面初步的探索得知,object_getClass(obj)返回的是obj中的isa指针;而[obj class]则分两种情况:一是当obj为实例对象时,[obj class]中class是实例方法:- (Class)class,返回的obj对象中的isa指针;二是当obj为类对象(包括元类和根类以及根元类)时,调用的是类方法:+ (Class)class,返回的结果为其本身。


参考

点击打开链接

对象和类






你可能感兴趣的:(基础知识,Runtime分析系列)