美文网首页iOS 技能
iOS-OC 对象 类对象 元类对象

iOS-OC 对象 类对象 元类对象

作者: 洧中苇_4187 | 来源:发表于2020-05-05 15:10 被阅读0次
    1. instance-对象
      创建两个对象,并打印,
      例一:
     //instance-对象
        NSObject *obj1 = [NSObject new];
        NSObject *obj2 = [NSObject new];
    

    可以看到两个对象的内存地址不一样

    2020-05-05 14:26:35.340051+0800 class对象[31421:2142371] 0x6000030e80f0 0x6000030e80d0
    

    结论:因为创建的对象可以有多个,如果对象里面有属性,不同的对象可以给相同的属性设置不同的值,比如一个MJPerson 有一个 age属性,我可以创建一个MJPerson 设置age值为18,同样创建一个MJPerson,age设置为20,

    1. class-类对象
      打印第一个例子中对象的类对象地址,
      注意:对象想要调用方法,必须通过isa指针找到类对象,从类对象里面寻找方法,因为对象里面只存储了成员变量,并没有方法,方法都存在类对象里面,而类方法存在元类对象里面,转成消息发送就是:
      objc_msgSend(person,@selector(instanceMethod));调用对象方法
      objc_msgSend([person class],@selector(classMethod));调用类方法
      例二:
        //class-类对象
        Class objClass1 = [obj1 class];
        Class objClass2 = [obj2 class];
        Class objClass3 = [NSObject class];
        Class objClass4 = object_getClass(obj1);
        Class objClass5 = object_getClass(obj2);
    

    打印结果如下: 可以看到对象的类对象内存地址一毛一样,证明,同一个类创建的多个对象,它的类对象只有一个-----为什么???
    原因:不管你创建多少对象,它的方法都是一样的,所有没有必要生成N多个类对象,只需要一份类对象,保存它所有的方法

    2020-05-05 14:26:35.339948+0800 class对象[31421:2142371] 0x7fff89be1d00 0x7fff89be1d00 0x7fff89be1d00 0x7fff89be1d00 0x7fff89be1d00
    
    1. metaClass-元类对象
      既然有了类对象,为什么还需要元类对象?
      注意:下面打印的objMetaClass3objMetaClass3都不是元类对象,objc_getClass()这个方法只能得到类对象,原因可以看我的上一篇文章:传送门
      例三:
        //metaclass-元类对象
        Class objMetaClass1 = objc_getMetaClass("NSObject");
        Class objMetaClass2 = object_getClass(objClass4);
        Class objMetaClass3 = objc_getClass("NSObject");
        Class objMetaClass4 = [[[NSObject class]class]class];
    
    

    打印结果如下:

    2020-05-05 14:26:35.339815+0800 class对象[31421:2142371] 0x7fff89be1cd8 0x7fff89be1cd8 0x7fff89be1d00 0x7fff89be1d00
    

    类对象的地址 和元类对象的地址又不同,我们知道-->
    一个类包含有几个大块:
    --成员变量数组
    --协议数组
    --类方法数组
    --对象方法数组
    --分类数组
    --局部变量数组

    附送一个编译命令行:
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc MJPerson.m -o MJPerson.cpp
    xcrun :x->Xcode
    xcrun -sdk iphoneos :指派Xcode生成 iphoneos 系统的文件
    clang : Mac自带的编译器
    clang -arch arm64 :指定架构类型为arm64,这是64位的,iPhone 还有armv7,armv7s,armv6,这几个都是32位的,还有i386,这个针对模拟器的,
    -rewrite-objc MJPerson.m -o MJPerson.cpp : 重写 MJPerson.m 文件 输出成MJPerson.cpp

    struct _prop_t {
        const char *name;
        const char *attributes;
    };
    
    struct _protocol_t;
    
    struct _objc_method {
        struct objc_selector * _cmd;
        const char *method_type;
        void  *_imp;
    };
    
    struct _protocol_t {
        void * isa;  // NULL
        const char *protocol_name;
        const struct _protocol_list_t * protocol_list; // super protocols
        const struct method_list_t *instance_methods;
        const struct method_list_t *class_methods;
        const struct method_list_t *optionalInstanceMethods;
        const struct method_list_t *optionalClassMethods;
        const struct _prop_list_t * properties;
        const unsigned int size;  // sizeof(struct _protocol_t)
        const unsigned int flags;  // = 0
        const char ** extendedMethodTypes;
    };
    
    struct _ivar_t {
        unsigned long int *offset;  // pointer to ivar offset location
        const char *name;
        const char *type;
        unsigned int alignment;
        unsigned int  size;
    };
    
    struct _class_ro_t {
        unsigned int flags;
        unsigned int instanceStart;
        unsigned int instanceSize;
        const unsigned char *ivarLayout;
        const char *name;
        const struct _method_list_t *baseMethods;
        const struct _objc_protocol_list *baseProtocols;
        const struct _ivar_list_t *ivars;
        const unsigned char *weakIvarLayout;
        const struct _prop_list_t *properties;
    };
    
    struct _class_t {
        struct _class_t *isa;
        struct _class_t *superclass;
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;
    };
    
    struct _category_t {
        const char *name;
        struct _class_t *cls;
        const struct _method_list_t *instance_methods;
        const struct _method_list_t *class_methods;
        const struct _protocol_list_t *protocols;
        const struct _prop_list_t *properties;
    };
    
    

    类 和 元类 的区别就在于,元类(metaClass)当中存放了类方法,而类(Class)中存放类方法的数组为空,怎么验证???

    1. 创建一个类 包含类方法 和对象方法, 打印类的所有方法 和元类的所有方法,以MJPerson为例
      例四:
    @interface MJPerson : NSObject
    @property (nonatomic,strong)NSString *mj_person;
    - (void)testInstanceClass;
    + (void)testMetaClass;
    @end
    

    这个是打印类所有的方法的函数,使用时需要导入#import <objc/runtime.h>文件

    void DumpObjcMethods(Class clz)
    {
        unsigned int methodCount = 0;
        Method *methods = class_copyMethodList(clz, &methodCount);
        printf("Found %d methods on '%s'\n", methodCount, class_getName(clz));
    
        for (unsigned int i = 0; i < methodCount; i++)
        {
            Method method = methods[i];
    
            printf("\t'%s' has method named '%s' of encoding '%s'\n",
                   class_getName(clz),
                   sel_getName(method_getName(method)),
                   method_getTypeEncoding(method));
            /**
             *  Or do whatever you need here...
             */
        }
        free(methods);
    }
    

    例五:创建MJPerson类,打印它的所有方法

        MJPerson *person1 = [MJPerson new];//instance-对象
        Class personCls = object_getClass(person1);//class-类对象
        Class personMetaCls = object_getClass(personCls);//metaclass-元类对象
        BOOL isMetaCls = class_isMetaClass(personMetaCls);
        
        DumpObjcMethods(personCls);
        
        DumpObjcMethods(personMetaCls);
    
        NSLog(@"%p %p %p %d %@",
              person1,
              personCls,
              personMetaCls,
              isMetaCls,
              personMetaCls
              );
    

    结果如下:

    Found 4 methods on 'MJPerson'
        'MJPerson' has method named 'testInstanceClass' of encoding 'v16@0:8'
        'MJPerson' has method named 'mj_person' of encoding '@16@0:8'
        'MJPerson' has method named 'setMj_person:' of encoding 'v24@0:8@16'
        'MJPerson' has method named '.cxx_destruct' of encoding 'v16@0:8'
    Found 1 methods on 'MJPerson'
        'MJPerson' has method named 'testMetaClass' of encoding 'v16@0:8'
    2020-05-05 15:04:37.639828+0800 class对象[32370:2166200] 0x600001bf03f0 0x10d430618 0x10d4305f0 1 MJPerson
    

    根据结果可以看到,类方法有四个
    一个testInstanceClass实例方法,
    一个set,get方法,
    一个cxx_destruct--析构函数
    而元类对象只有一个方法 testMetaClass

    1. 如果 MJPerson有一个类方法+(void)test,有一个分类(Category)继承自NSObject,它里面有个-(void)test方法,[MJPerson test]会不会报错???
    @interface NSObject (Test)
    - (void)test;
    @end
    
    @implementation NSObject (Test)
    - (void)test{
        NSLog(@"%s",__func__);
    }
    @end
    
    @interface MJPerson : NSObject
    + (void)test;
    @end
    
    @implementation MJPerson
    @end
    这么调用:
        [MJPerson test];
    

    答案是不会报错,打印结果如下;

    2020-05-05 18:36:13.224881+0800 class对象[37802:2256463] -[NSObject(Test) test]
    

    原因:以本文为例,NSObject的元类superclass指针最后指向它自己的类对象,所以当类方法寻找不到的时候,NSObject的元类,就会到他类对象里面寻找,所以最终会调用到-(void)test对象方法,而不是报方法找不到的错误.
    对应的就是图中这根线:

    image.png

    相关文章

      网友评论

        本文标题:iOS-OC 对象 类对象 元类对象

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