OC对象的分类

(本文所运行的Demo是在集成了objc4源码基础上的,详见:gitHub

本文所写的项目详见:OCBasicDemo

基本概念

  • OC对象的分类
    • instance对象(实例对象)
    • class对象(类对象)
    • meta-class对 象(元类对象)

1.instance对象

  • instance对象就是通过alloc出来的对象,每次调用alloc都会开辟新的内存,都会产生新的instance对象(实例对象)
先定义一个Person自定义的类对象
@interface Person : NSObject{
   @public
   int _age;
   int  _number;
}
@end
@implementation Person
@end

---------使用--------
int main(int argc, const char * argv[]) {
   @autoreleasepool {
       //instance对象
       Person *p1 = [[Person alloc]init];
       p1->_age = 10;
       p1->_number = 100;
       
       Person *p2 = [[Person alloc]init];
       p2->_age = 12;
       p2->_number = 90;
       
       NSLog(@"%p",p1);//0x1005222a0
       NSLog(@"%p",p2);//0x1005220b0
       
       NSLog(@"%p",&(p1->_age));    //0x1005222a8
       NSLog(@"%p",&(p1->_number)); //0x1005222ac
       
       NSLog(@"%p",&(p2->_age));    //0x1005220b8
       NSLog(@"%p",&(p2->_number)); //0x1005220bc
   
       NSLog(@"End");
   }
   return 0;
}
  • p1和p2是Person的instance对象(实例化出来的)
  • 他们所占不同的内存空间(根据他们打印的内存地址就可以看出)
    分析:
    • p1的首地址和p2的首地址不一样
    • p1的_age地址和p1的首地址(相差8个字节)(0x1005222a8-0x1005222a0)
    • p1的_number地址在_age地址之后排的(加4个字节)
    • p2的_age地址和p2的首地址(相差8个字节)(0x1005220b0-0x1005220b8)
      查看内存:
      image

      我们发现:上述描述的结论

使用:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o myMain.cpp
转为C\C++代码:

struct Person_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _age;
    double _height;
};

struct NSObject_IMPL {
    Class isa;
};

我们可以推测出下面的图:(instance对象的初结构)


image
  • instance对象在内存中存储的信息包括
    • isa(这里我们暂且将它定为isa,在后面的章节会具体的分析社么是isa以及isa的作用)
    • 其他的成员变量

2.class

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *object1 = [[NSObject alloc] init];
        NSObject *object2 = [[NSObject alloc] init];
        
        Class objectClass1 = [object1 class];
        Class objectClass2 = [object2 class];
        Class objectClass3 = object_getClass(object1);
        Class objectClass4 = object_getClass(object2);
        Class objectClass5 = [NSObject class];
        
        NSLog(@"%p %p",object1,object2);//0x101803170  0x101804f60
    
        NSLog(@"%p %p %p %p %p",
              objectClass1,//0x100b14140
              objectClass2,//0x100b14140
              objectClass3,//0x100b14140
              objectClass4,//0x100b14140
              objectClass5);//0x100b14140
        NSLog(@"End");
    }
    return 0;
}

发现:

  • objectClass1~objectClass5的内存地址都是一样的(也就是说是同一个东西),他们都是NSObject的class对象(类对象)
  • 每个类在内存中有且只有一个类对象
    接下来我们来代码分析下:
    点击Class进入系统的库(这里不是自己集成的objc4-750的哟),发现是:
    *typedef struct objc_class Class;
    再次点击:objc_class进入
    发现:
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

从这个里面我们可以看到:
一个类信息里面包含的东西如下:

image

点击Class进入自己集成的objc4-750的源文件里面

1.点击Class----------->
typedef struct objc_class *Class;

2.点击 objc_class进入----------->
struct objc_class : objc_object {
    //Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    //此处省略代码...
    
3.点击class_rw_t----------->
struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
    //此处省略代码...
    
4.点击class_ro_t----------->
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

从源码里面:我们也可以找到对应的信息


3.meta-class(元类)

  • 在main.m函数里面
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
       NSObject *object1 = [[NSObject alloc] init];
        Class objectClass1 = [object1 class];
        Class objectClass2 = object_getClass(object1);
        Class objectClass3 = [NSObject class];
        
        // meta-class对象,元类对象
        // 将类对象当做参数传入,获得元类对象
        Class objectMetaClass1 = object_getClass(objectClass1);
        Class objectMetaClass2 = object_getClass(objectClass2);
        Class objectMetaClass3 = object_getClass(objectClass3);
        
        NSLog(@"%p %p %p",
              objectClass1,//0x100b14140
              objectClass2,//0x100b14140
              objectClass3 //0x100b14140
              );
        
        NSLog(@"%p %p %p",
              objectMetaClass1,//0x100b140f0
              objectMetaClass2,//0x100b140f0
              objectMetaClass3 //0x100b140f0
              );
              
        //class方法返回的一直是class对象(类对象)
        Class objectMetaClass4 = [[[NSObject class] class] class];
        NSLog(@"%p",objectMetaClass4);//0x100b14140
        
        NSLog(@"End");
        
    }
    return 0;
}
  • 发现objectMetaClass1~objectMetaClass3 他们是同一个
  • objectMetaClass1是NSObject的meta-class的对象(元类对象)
  • 每一个类在内存中有且只有一个meta-class对象
  • meta-class对象和class对象的内存结构是一样的,只是他们不同的用途不一样(见后章节:会有细讲)
  • 在内存中主要存储的信息:
    • isa指针
    • superclass指针
    • 类的方法信息 (classMethods)
    • ...(等)

补充:

我们在objc的NSObject.mm文件里面看到(或者通过command+鼠标左键,进入打断点可知)
//类的class方法
+ (Class)class {
    return self;
}

//实例对象的class方法
- (Class)class {
    return object_getClass(self);
}

在object-class.mm文件里面(或者通过command+鼠标左键,进入打断点可知)
//方法 object_getClass
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

分析:

   1.
   Class objectMetaClass4 = [[[NSObject class] class] class];
   NSLog(@"%p",objectMetaClass4);//0x100b14140
   分析:
   NSObject是一个类对象,所以它掉的是+Class方法,调用class方法,一直返回的是自己(类对象)
   
   2.
   Class objectMetaClass4 = [[[object1 class] class] class];
   NSLog(@"%p",objectMetaClass4);//0x100b14140
   分析:
   object1是一个实例对象,调用-Class方法,它返回的是一个类对象(object_getClass(self)),然后类对象继续调用+Class,+Class,最终和上面的结果一致,返回的永远是类对象

由上我们可知:(+/-)class方法返回的一直是class对象(类对象)


4.查看Class是否为meta-class

    BOOL result1 = class_isMetaClass([object1 class]);
    BOOL result2 = class_isMetaClass([NSObject class]);
    BOOL result3 = class_isMetaClass(object_getClass(([NSObject class])));
    NSLog(@"%d %d %d",
            result1,//0
            result2,//0
            result3 //1
            );
    分析:result1、result2 方法返回的永远是类对象,所以他们永远也不是元类对象
    result3:传入的是类对象,返回的是元类对象

5.objc_getClass方法
这里我们先记住结论:(后面会在内存章节会具体的分析底层原理)

Class object_getClass(id obj)
1> 传入的obj可能是instance对象、class对象、meta-class对象
2> 返回值
a) 如果是instance对象,返回class对象
b) 如果是class对象,返回meta-class对象
c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象

6:定义一个自定义对象来看

 @interface Person:NSObject
 @endz
 @implementation Person
 @end

 Person   *p = [[Person alloc]init];//0x102141190
 Class pClass = object_getClass(p);//0x1000011f8
 Class metaPClass = object_getClass(pClass);//0x1000011d0
 BOOL res = class_isMetaClass(metaPClass);//1
 NSLog(@"%p %p %p %d",p,pClass,metaPClass,res);

友情链接:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,861评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,263评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,033评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,999评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,000评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,483评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,850评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,827评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,366评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,404评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,525评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,130评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,853评论 3 338
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,293评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,426评论 1 276
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,082评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,590评论 2 366

推荐阅读更多精彩内容