美文网首页工作生活
深入理解Objective-C中实例、类对象、元类对象之间的关系

深入理解Objective-C中实例、类对象、元类对象之间的关系

作者: imzzb | 来源:发表于2019-07-02 15:32 被阅读0次

    实例、类对象、元类对象之间的关系可以用下面这张经典的图来展示:

    class-diagram.jpg

    总结:

    • 实例的isa指向类对象
    • 类对象的isa指向元类对象
    • 元类对象的isa指针指向根元类对象
    • 根元类的父类是根类对象

    根元类的父类是根类对象意味着什么呢?

    我们知道如果调用类方法,会沿着元类对象的继承链依次向上查找方法的实现。

    因为跟元类的父类是根类对象,所以如果在跟元类中无法查找到该方法的实现,会到根类对象中去查找。

    而根类对象中的方法都是实例方法。

    所以这意味着,如果一个类的类方法没有被实现,就会去调用它的根类(NSObject)中的同名实例方法。

    我们用代码验证一下。

    NSObject新建一个Category,并添加一个名为foo的实例方法。方法的实现只是简单打印foo字符串

    //NSObject+Foo.h
    
    #import <Foundation/Foundation.h>
    @interface NSObject (Foo)
    - (void)foo;
    @end
    
    
    //NSObject+Foo.m
    
    #import "NSObject+Foo.h"
    @implementation NSObject (Foo)
    - (void)foo {
        NSLog(@"foo foo");
    }
    @end
    

    新建一个UIView的子类,并在这个子类的.h文件中声明一个名为foo类方法,但在.m文件中并不实现它。

    // CView.h
    
    #import <UIKit/UIKit.h>
    @interface CView : UIView
    + (void)foo;
    @end
    
    // CView.m
    #import "CView.h"
    @implementation CView
    
    @end
    
    

    然后调用这个类方法[CView foo];

    按理说我们没有实现+ (void)foo;程序应该崩溃。但是,实际上会调用NSObject的Category中的实例方法- (void)foo,打印出"foo foo",非常神奇

    使用super关键字调用方法的时候,发生了什么

    如果上面提到的CView有这样一个初始化方法:

    #import "CView.h"
    
    @implementation CView
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            NSLog(@"%@",NSStringFromClass([self class]));
            NSLog(@"%@",NSStringFromClass([super class]));
        }
        return self;
    }
    @end
    

    问:如果调用这个初始化方法,打印的结果是什么?

    要解答这个问题,我们需要了解消息传递的过程?

    我们知道,在OC中方法调用[obj method]会被编译器编译为objc_msgSend,它的定义为objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

    前两个参数是固定的,分别是消息的接受者和方法选择器。

    那么super关键字调用方法会被转换为objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...),第一个参数为objc_super指针

    objc_super是下面的数据结构:

    /// Specifies the superclass of an instance. 
    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained _Nonnull id receiver;
    
        /// Specifies the particular superclass of the instance to message. 
    #if !defined(__cplusplus)  &&  !__OBJC2__
        /* For compatibility with old objc-runtime.h header */
        __unsafe_unretained _Nonnull Class class;
    #else
        __unsafe_unretained _Nonnull Class super_class;
    #endif
        /* super_class is the first class to search */
    }
    
    

    其中的receiver是类的一个实例,也就是self。

    也就是说,最终消息的接受者还是self这个实例。只不过,objc_msgSendSuper查找方法实现的过程,是从self的父类对象开始的。

    但是class方法是在根类NSObject中定义的,所以不论是[self class]还是[super class]都会调用NSObject中的实现,而且消息的接收者也是一样的,都是self这个实例。所以打印结构也是一样的,都打印self的类名。

    相关文章

      网友评论

        本文标题:深入理解Objective-C中实例、类对象、元类对象之间的关系

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