目录
想必对象大家都清楚(不是恋爱对象哦~),那么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