iOS 技能

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

2020-05-05  本文已影响0人  洧中苇_4187
  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
上一篇下一篇

猜你喜欢

热点阅读