runtime - iOS类对象、实例对象、元类对象

理解类与对象的本质对于掌握一门语言是相当重要的,本文将从结构类型的角度探讨OC的类对象、实例对象、元类对象(Meta Class)。css

咱们先看一张图:git

 

 
iOS类、实例、元类关系图.jpg
  • 每一个 Class 都有一个 isa 指针指向一个惟一的 Meta Class
  • 每个 Meta Class 的 isa 指针都指向最上层的 Meta Class,即 NSObject 的 MetaClass,而最上层的 MetaClass 的 isa 指针又指向本身

1.类对象

类对象是由程序员定义并在运行时由编译器建立的,它没有本身的实例变量,这里须要注意的是类的成员变量和实例方法列表是属于实例对象的,但其存储于类对象当中的。咱们在objc.h下看看Class的定义:程序员

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

可见,Class是指向C的结构体objc_class的指针,咱们再看一下objc_class的定义(runtime.h):github

 
 
struct objc_class {
    Class _Nonnull isa;                                     // 指向所属类的指针(_Nonnull)
    Class _Nullable super_class;                      // 父类  
    const char * _Nonnull name;                      // 类名(_Nonnull)
    long version;                                              // 类的版本信息(默认为0)   
    long info;                                                   // 类信息(供运行期使用的一些位标识)   
    long instance_size;                                    // 该类的实例变量大小
    struct objc_ivar_list * _Nullable ivars;        // 该类的成员变量链表
    struct objc_method_list * _Nullable * _Nullable methodLists;  // 方法定义的链表                  
    struct objc_cache * _Nonnull cache;                        // 方法缓存
    struct objc_protocol_list * _Nullable protocols;        // 协议链表
} ;
 
 
  • isa指针是和Class同类型的objc_class结构指针,类对象的指针指向其所属的类,即元类。元类中存储着类对象的类方法,当访问某个类的类方法时会经过该isa指针从元类中寻找方法对应的函数指针。
  • super_class为该类所继承的父类对象,若是该类已是最顶层的根类(如NSObject或NSProxy), 则 super_class为NULL。
  • ivars是一个指向objc_ivar_list类型的指针,用来存储每个实例变量的地址。
  • info为运行期使用的一些位标识,好比:CLS_CLASS (0x1L)表示该类为普通类, CLS_META (0x2L)则表示该类为元类。
  • methodLists用来存放方法列表,根据info中的标识信息,当该类为普通类时,存储的方法为实例方法;若是是元类则存储的类方法。
  • cache用于缓存最近使用的方法。系统在调用方法时会先去cache中查找,在没有查找到时才会去methodLists中遍历获取须要的方法。

2.实例对象

实例对象是咱们对类对象alloc或者new操做时所建立的,在这个过程当中会拷贝实例所属类的成员变量,但并不拷贝类定义的方法。调用实例方法时,系统会根据实例的isa指针去类的方法列表及父类的方法列表中寻找与消息对应的selector指向的方法。一样的,咱们也来看下其定义(objc.h):objective-c

 
 
/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
 
 

objc_object这个结构体只有一个isa变量,指向实例对象所属的类。任何带有以指针开始并指向类结构的结构均可以被视做objc_object, 对象最重要的特色是能够给其发送消息. NSObject类的alloc和allocWithZone:方法使用函数class_createInstance来建立objc_object数据结构。缓存

 
 
/// A pointer to an instance of a class.
typedef struct objc_object *id;
 
 

id是一个objc_object结构类型的指针。该类型的对象能够转换为任何一种对象,相似于C语言中void *指针类型的做用(objc.h)。ruby

3.元类对象(MetaClass)

顾名思义,元类对象便是描述类对象的类,每一个类都有本身的元类,也就是结构体objc_class中isa指向的类。Objective-C的类方法是使用元类的根本缘由,由于其中存储着对应的类对象调用的方法即类方法。数据结构

 

 
类存储示意图.jpg

 

因而可知,在给实例对象或类对象发送消息时,寻找方法列表的规则为:函数

*当发送消息给实例对象时,消息是在寻找这个对象的类的方法列表(实例方法)ui

*当发送消息给类对象时,消息是在寻找这个类的元类的方法列表(类方法)

全部元类都有一个根元类,好比全部NSObject的子类的元类都会以NSObject的元类做为他们的类。全部的元类使用根元类做为他们的类,根元类的元类则就是它本身,也就是说根元类的isa指针指向他本身。

这里咱们能够进一步研究一下官方技术文档runtime.h:

 
 
OBJC_EXPORT Class _Nullable
object_getClass(id _Nullable obj) 
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
 
 

这里object_getClass与NSObect.h中的类方法 +(Class)class;和实例方法+ (Class)class;有什么不一样呢?请看源码:

 
 
Class object_getClass(id obj) {
    return _object_getClass(obj);
}

static inline Class _object_getClass(id obj) {

    #if SUPPORT_TAGGED_POINTERS

    if (OBJ_IS_TAGGED_PTR(obj)){
        uint8_t slotNumber = ((uint8_t)(uint64_t) obj) & 0x0F;
        Class isa = _objc_tagged_isa_table[slotNumber];
        return isa;
    }

    #endif

        if (obj) return obj->isa;   
        else return Nil;

}
 
 

_object_getClass函数就是返回对象的isa指针,也就是返回该对象所指向的所属类。

 
 
+ (Class)class {
    return self; // 返回自身指针
}

- (Class)class {
    return object_getClass(self); // 调用'object_getClass'返回isa指针
}
 
 

总结一下实例对象,类对象以及元类对象之间的isa指向和继承关系的规则为:
规则一: 实例对象的isa指向该类,类的isa指向元类(metaClass)
规则二: 类的superClass指向其父类,若是该类为根类则值为nil
规则三: 元类的isa指向根元类,若是该元类是根元类则指向自身
规则四: 元类的superClass指向父元类,若根元类则指向该根类

 

 

四、下面咱们来看一个例子,方便更好的来理解元类的概念
同时咱们首先理解几个知识点

object_getClass(实例对象) == [实例对象 class]
[类对象 class] == 类对象
object_getClass(类对象) == 类对象的isa == 元类
object_getClass(类对象) != [类对象 class]


添加方法,实际上是在 类对象/实例对象 中的isa指针的类中添加

 

首先定义一个 Obj的类
----------------------------------------------------
@interface Obj : NSObject
//实例方法
- (void)print1;
//类对象方法
+ (void)classPrint;
@end
@implementation Obj
//这里不去实现它,后面咱们会经过runtime的知识添加方法
@end
 下面咱们切换当控制器中 ----------------------------------------------------
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib.
 Obj * obj = [Obj new]; Class class11 = object_getClass(obj); Class class12 = [obj class]; //这个是获取当前的类的对象,实例是就是isa指向的类对象,可是类对象指向确是本身自己
 NSLog(@"class1 :%p,class2 = %p",class11,class12); //class1 :0x1077b8f00,class2 = 0x1077b8f00
 Class class21 = object_getClass(class11);//Son的Class的元类
    Class class22 = [class12 class];//仍是自己Son的Class
    Class classMeta= objc_getMetaClass(object_getClassName(obj));//Son的Class元类
 NSLog(@"class1 :%p,class2 = %p,classMeta = %p",class21,class22,classMeta); //class1 :0x1077b8ed8,class2 = 0x1077b8f00,classMeta = 0x1077b8ed8 //在给对象或者类添加方法的时候,实际上是给isa 指向的类添加方法,就是说 一个普通的对象是给它的class添加方法,而 一个普通的类对象,须要添加方法实际上是给它isa指向的 元类添加方法 //给obj实例对象 添加 方法
 class_addMethod(class11, @selector(print1), (IMP)IMPFunc, NULL); [obj print1]; //给obj的类对象添加方法
 class_addMethod(class21, @selector(classPrint), (IMP)IMPMetaClassFunc, NULL); [Obj classPrint]; } void IMPFunc(id self ,SEL cmd) { NSLog(@"print1"); } void IMPMetaClassFunc(id self ,SEL cmd) { NSLog(@"IMPMetaClassFunc"); }

 

 

 

参考文章

1.[格物致知iOS类与对象] https://mp.weixin.qq.com/s/iBELEyUfnShnLhS5xJh4mQ

2.[清晰理解Objective-C元类]http://blog.csdn.net/beclosedtomyheart/article/details/50164353

3.[Objective-C Runtime 运行时之一:类与对象]http://southpeak.github.io/2014/10/25/objective-c-runtime-1/

相关文章
相关标签/搜索