美文网首页iOS面试iOS
iOS 类和元类的关系

iOS 类和元类的关系

作者: 凤栖林 | 来源:发表于2017-08-30 16:51 被阅读0次

    事情的始末是这样的,同学想验证一下resolveClassMethod是否执行(resolveClassMethod是一个对象调用一个不存在类方法时,会执行此方法,不懂的要恶补一下了,可以看我这篇文章:Objective-C消息转发),然后发来了如下代码:

    [NSObject performSelector:@selector(hehe)];
    

    当时看完之后产生了疑惑,performSelector是一个实例方法,NSObject是一个类,难道编译不会报错吗?后来亲测发现确实不会报错。

    然后开始了我们今天的故事:
    我们都知道下面这样写一定会出错

    @interface TestObject : NSObject
    @end
    @implementation TestObject
    - (void)method1{
           [TestObject method2];//这样调用一定会编译错误
    }
    - (void)method2{}
    

    然而这样写却不会报错

    @interface TestObject : NSObject
    @end
    @implementation TestObject
    - (void)method{
       [NSObject performSelector:@selector(hehe)];
    }
    

    甚至于这样写也不会报错
    创建一个NSObject的类目

    @interface NSObject (hehe)
    +(void)run;
    @end
    @implementation NSObject (hehe)
    -(void)run{
        NSLog(@"run.....");
    }
    @end
    

    然后调用

    [NSObject run];
    

    这是为什么哪?
    看一张关系图

    图片.png

    (此图来源自网络)
    假设A类继承自B类,B类继承自NSObject
    A便是途中的Subclass(class),B便是图中的Superclass(class),NSObject便是Root class(class);
    A *a = [A new];
    其实A和a一样,也是对象,A称为类对象,a称为实例对象
    每一个类对象都有一个isa指针

     Class isa  OBJC_ISA_AVAILABILITY;
    

    这个isa指针的指向就是该类对象的元类,每一个类都是它的元类的对象,元类是对类对象的描述,就像类是普通实例对象的描述一样。

    每一个类里面声明的类方法,其本质就是把该类方法放到元类的方法列表上面,所以类在调用类方法时,可以想象成是元类的对象在调用一个实例方法。

    A的父类是B,A的元类的父类是B元类的父类,B的父类是NSObject,NSObject的父类是nil,B元类的父类是NSObject的元类;特别注意的一点,NSObject的元类的父类是NSObject,NSObject的isa指针又指向NSObject的元类,所以在NSObject里面的所有方法,NSObject的元类也都拥有,1、所以用NSObject 调用任意NSObject里面的实例方法都是可以成功的,2、这也就解释了上面的声明里面是+(void)run;类方法,实现里面是-(void)run{ NSLog(@"run.....");}实例方法,调用却不会崩溃。

    类和元类是一个闭环,实例指向类,类指向元类,元类指向跟元类,跟元类指向自身,根元类的父类是NSObject

    元类是 Class 对象的类。每个类(Class)都有自己独一无二的元类(每个类都有自己第一无二的方法列表)。这意味着所有的类对象都不同。

    NSObject里面的所有实力方法,任意类都可以通过类方法调用。

    所有的meta-class使用基类的meta-class作为自己的基类,对于顶层基类的meta-class也是一样,只是它指向自己而已

    相关文章

      网友评论

        本文标题:iOS 类和元类的关系

        本文链接:https://www.haomeiwen.com/subject/txfxjxtx.html