详解Class和MetaClass

目录

想必对象大家都清楚(不是恋爱对象哦~),那么Class又是什么?这中文意思大家都是知道是“类”,可为什么偏偏就有这么一个结构体叫Class?Objective-C Runtime里面有个api: id objc_getMetaClass(const char *name),这个MetaClass又是个什么鬼?它究竟和Class有什么关系?这一切的背后究竟是究竟是人性的扭曲还是。。。。咳咳,扯远了~我开始也是怀着这些疑问去研究Class和MetaClass,现在总结出了这篇文章,希望能帮到大家。

isa指针

在Xcode里面能看到NSObject里面有个isa指针:

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

再点进去看Class是什么:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

再看看objc_class:

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

从注释我们可以看到Class代表的是一个Objective-C的类,也就是说,你这个当前这个实例化了的对象的isa指针是代表着这个对象是个什么类。

其实,在Objective-C中,任何对象都会有isa指针,任何类的定义都是对象。

Class是一个指向结构体objc_class的指针,objc_class也有一个isa指针。既然上面说到NSObject对象的isa是代表着类(Class),那这个Class结构体里的isa指针又是代表什么呢?没错,就是我们要说的metaClass(元类)~

究竟什么是metaClass?

要想搞清metaClass,首先要把Class屡清楚才行。每个对象都有一个类(Class)。这是一个基本的面向对象概念,但在Objective-C中,它也是数据的基本部分。当我们想给Objective-C发消息的时候(调用方法),比如下面这样:

[@"a" stringByAppendingString:@"b"];

Runtime就要去找到stringByAppendingString这个方法,先通过isa指针以获取对象的Class(在这个例子就是NSString),然后正如我们看到objc_class的数据结构,继续从methodLists(方法列表)中找到响应的方法。如果这个Class里面没有想要找的方法,那就继续从super_class中查找继承的方法。从methodLists找到这个方法以后,Runtime就会调用该方法的IMP(方法的实现)。所以,Class里定义了一些你可以给对象发送的方法。

我们继续来看metaClass。Class在Objective-C里面也是一个对象(结构体),它也有isa指针,意味着你也可以给这个对象(Class)发消息。再看下面的例子:

[NSString stringWithFormat:@"%@",@"hello"];

我们调用了NSString类方法stringWithFormat,Runtime会通过Class(NSString类)的isa指针找到另一个Class结构体(其实是metaClass),再在这另一个Class结构体的methodLists查找想要的方法(stringWithFormat)。那么,我们可以引出metaClass的定义:metaClass是Class对象的类。

metaClass的isa指针是指向根metaClass,如果该metaClass是个根metaClass,则isa指针指向他自己。而metaClass的super_class是指向该metaClass的父类(也是个metaClass),如果该 metaClass 是根 metaClass则super_class指向该 metaClass 对应的类。

总结起来就有了这么一张示意图:
这里写图片描述

代码示例

blabla说了这么一大堆,怎么能少得了写几行代码,来实践一下呢?
先来这么几行代码:

@interface Test: UIViewController

@end

Test *testObj = [[Test alloc] init];
NSLog(@"testObj address: %p.",testObj);

Class currentClass = object_getClass(testObj);
for (int i = 0; i < 5; i ++) {
    NSLog(@"%d time isa points to address: %p",i+1,currentClass);
    currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class address: %p",[NSObject class]);
NSLog(@"NSObject's metaClass address: %p",object_getClass([NSObject class]));

NSLog(@"Test's class address: %p.",[Test class]);
Class superClass = class_getSuperclass([Test class]);
for (int i = 0; i < 5; i ++) {
    NSLog(@"%d time super_class points to addrss: %p",i+1,superClass);
    superClass = class_getSuperclass(superClass);
}
NSLog(@"NSObject's superClass address: %p",class_getSuperclass([NSObject class]));

来看看控制台的输出:

Test[3541:930356] testObj address: 0x104d0c0b0.
Test[3541:930356] 1 time isa points to address: 0x10488cee8
Test[3541:930356] 2 time isa points to address: 0x10488cf10
Test[3541:930356] 3 time isa points to address: 0x22b3a4ec8
Test[3541:930356] 4 time isa points to address: 0x22b3a4ec8
Test[3541:930356] 5 time isa points to address: 0x22b3a4ec8
Test[3541:930356] NSObject's class address: 0x22b3a4ea0
Test[3541:930356] NSObject's metaClass address: 0x22b3a4ec8
Test[3541:930356] Test's class address: 0x10488cee8.
Test[3541:930356] 1 time super_class points to addrss: 0x22b53af30
Test[3541:930356] 2 time super_class points to addrss: 0x22b53ae18
Test[3541:930356] 3 time super_class points to addrss: 0x22b3a4ea0
Test[3541:930356] 4 time super_class points to addrss: 0x0
Test[3541:930356] 5 time super_class points to addrss: 0x0
Test[3541:930356] NSObject's superClass address: 0x0

可见:

  • Test的class地址是0x10488cee8
  • Test的metaClass地址是0x10488cf10
  • Test的metaClass的父metaClass(metaClass的isa指针所指)地址是0x22b3a4ec8

这时,已经到了根metaClass,而根metaClass的isa都是指向同一个地址,而且也是根metaClass的地址,正好也验证了上面示意图的isa的指向。

  • Test的super_class(UIViewController的Class)地址是0x22b53af30
  • Test的super_class的super_class(UIResponder的Class,因为UIViewController继承UIResponder)地址是0x22b53ae18
  • Test的super_class的super_class的super_class(NSObject的Class,因为UIResponder继承NSObject)地址是0x22b3a4ea0

到了后面的super_class都已经是0x0,都指向了nil,正好也验证了上面示意图的super_class的指向。

还不过瘾?那就再来:

@interface Parent : NSObject

+ (void)parent_sta_method{

}

-(void)parent_method{

}

@end

@interface Child :Parent

+ (void)child_sta_method{
    NSLog(@"");
}

-(void)child_method{

}

@end
Class metaClass = objc_getMetaClass("Child");
Child *child = [[Child alloc] init];
Class classForInstance = [child class];

/****-----------------child_sta_method---------------------- ***/
NSLog(@"****-----------------child_sta_method---------------------- ***");
if (class_respondsToSelector(metaClass, @selector(child_sta_method))) {
    NSLog(@"MetaClass has \"child_sta_method\" method");
} else{
    NSLog(@"MetaClass dosn't have \"child_sta_method\" method");
}
if (class_respondsToSelector(classForInstance, @selector(child_sta_method))) {
    NSLog(@"Class has \"child_sta_method\" method");
} else{
    NSLog(@"Class dosn't have \"child_sta_method\" method");
}
NSLog(@"------------------------------------------------------------");

/****-----------------child_method------------------------- ***/
NSLog(@"****-----------------child_method---------------------- ***");
if (class_respondsToSelector(metaClass, @selector(child_method))) {
    NSLog(@"MetaClass has \"child_method\" method");
} else{
    NSLog(@"MetaClass dosn't have \"child_method\" method");
}
if (class_respondsToSelector(classForInstance, @selector(child_method))) {
    NSLog(@"Class has \"child_method\" method");
} else{
    NSLog(@"Class dosn't have \"child_method\" method");
}
NSLog(@"------------------------------------------------------------");

/****-----------------parent_sta_method---------------------- ***/
NSLog(@"****-----------------parent_sta_method---------------------- ***");
if (class_respondsToSelector(metaClass, @selector(parent_sta_method))) {
    NSLog(@"MetaClass has \"parent_sta_method\" method");
} else{
    NSLog(@"MetaClass dosn't have \"parent_sta_method\" method");
}
if (class_respondsToSelector(classForInstance, @selector(parent_sta_method))) {
    NSLog(@"Class has \"parent_sta_method\" method");
} else{
    NSLog(@"Class dosn't have \"parent_sta_method\" method");
}
NSLog(@"------------------------------------------------------------");

/****-----------------parent_method---------------------- ***/
NSLog(@"****-----------------parent_method---------------------- ***");
if (class_respondsToSelector(metaClass, @selector(parent_method))) {
    NSLog(@"MetaClass has \"parent_method\" method");
} else{
    NSLog(@"MetaClass dosn't have \"parent_method\" method");
}
if (class_respondsToSelector(classForInstance, @selector(parent_method))) {
    NSLog(@"Class has \"parent_method\" method");
} else{
    NSLog(@"Class dosn't have \"parent_method\" method");
}
NSLog(@"------------------------------------------------------------");

来瞅瞅控制台的结果:

Test[3606:945360] ****-----------------child_sta_method---------------------- ***
Test[3606:945360] MetaClass has "child_sta_method" method
Test[3606:945360] Class dosn't have "child_sta_method" method
Test[3606:945360] ------------------------------------------------------------
Test[3606:945360] ****-----------------child_method---------------------- ***
Test[3606:945360] MetaClass dosn't have "child_method" method
Test[3606:945360] Class has "child_method" method
Test[3606:945360] ------------------------------------------------------------
Test[3606:945360] ****-----------------parent_sta_method---------------------- ***
Test[3606:945360] MetaClass has "parent_sta_method" method
Test[3606:945360] Class dosn't have "parent_sta_method" method
Test[3606:945360] ------------------------------------------------------------
Test[3606:945360] ****-----------------parrent_method---------------------- ***
Test[3606:945360] MetaClass dosn't have "parent_method" method
Test[3606:945360] Class has "parent_method" method
Test[3606:945360] ------------------------------------------------------------

很明显,

  • metaClass是能找得到类方法的,在前面提到methodLists里面,如果是父类的类方法,就会通过metaClass的superClass去找到父metaClass,再从中找相应的类方法;
  • metaClass是找不到对象方法的;
  • Class是可以找到对象方法的,在前面提到methodLists里面,如果是父类的对象方法,就会通过Class的superClass去到找到父类的Class,再从中找到相应的对象方法;
  • Class是找不到类方法的;

其实,这正是体现了面向对象三大特性之一:继承性。


参考博客:
【1】http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html

猜你喜欢

转载自blog.csdn.net/u014686721/article/details/82189342