类的分析、探索

类的isa探究

  • 准备研究类LGPerson类。沿用探究oc对象的思路,使用lldb先从类的内存、isa来进行观察。
    我们已经知道对象isa是指向其Class的。那Classisa右指向哪里呢。
类内存及指针地址

从结果发现LGPerson类的isa指向的是LGPerson自己。我们知道LGPerson的实例对象personisa也是指向LGPerson的。那么两个isa的地址应该是指向了同一块内存,那么两个isa地址是否相同呢。

LGPerson & person

从图中可以到两个isa的地址是不相同。但是其两个isa都指向了LGPerson

  • person对象中,其isa的指针地址& ISA_MASK后得到的是person的类LGPerson
  • LGPerson类中,其isa的指针地址& ISA_MASK后得到的是LGPerson类的类。
  • Apple中,称LGPerson类的类为元类
元类
  • 对象isa指向其实也是一个对象。可称之为类对象,其isa指向apple定义的元类
  • 元类系统给的,其定义、创建都由编译器自动完成,类的归属来自于元类
  • 元类类对象,每个都有一个独一无二的的元类用来存储类的相关信息
  • 元类本身是没有名称的,由于其与类相关,所以使用了同类名相同的名称

那么按照上面逻辑继续下去LGPerson类类的isa指向哪里呢。

isa指向

从上面看到对象的isa指向LGPerson 类对象isa指向元类,都是LGPerson,最终isa最终指向了一个NSObject类。

  • 那么这个NSObject和我们内存中NSObject一样吗。
  • 类对象内存中又存在多少呢。

查看NSObject的信息

NSObject类的isa

NSObject类isa的指针地址和上面最后的根元类的isa指针地址相同。由此可见内存中NSObjectisa最终指向的NSObject应该是同一个。

创建多个类对象输出内存地址查看。

 Class class1 = [LGPerson class];
 Class class2 = [LGPerson alloc].class;
 Class class3 = object_getClass([LGPerson alloc]);
 NSLog(@"\n%p-\n%p-\n%p-\n%p", class1, class2, class3);
类地址

可以看出其地址是完全相同的,因此可以得出类对象内存中只存在一份,那么根元类NSObject同样在内存中只存在一个份其isa指向自己。

isa指向
isa流程图.png
  • 实例对象(Instance of Subclass)isa指向类(class)
  • 类对象(class)isa指向元类(metal class)
  • 元类(metal class)isa指向根元类(root metal class)
  • 根元类(root metal class)isa指向其自己

注意:实例对象之间没有继承关系之间才存在继承关系NSObject继承自nil

类结构分析

在探究isa与cls关联的文章中使用Clangmain.m转化成c++文件探究对象的本质。同样用此去探究类Class的是什么。

typedef struct objc_class *Class;

c++文件可以发现Class是一个objc_class的结构体。接下来进入apple提供的objc4源码中探究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;//废除

但是此定义确是已经被废除。寻找新的定义objc-runtime-new.h

新objc_class定义

内容太长了,只截了少部分objc4-781源码
在源码中搜索发现objc_object会发也有两个版本

//位于objc.h文件
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
//位于objc-private.h文件
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();
    ....
}

结合编译的c++main.cpp的文件中显示,使用objc.h文件中定义的版本。

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

结合objc4源码和main.cpp底层编译发现:

  • objc_class继承自objc_objectobjc_object结构体,拥有isa,所以
    objc_class也有isa

  • NSObject中的isa是由Class定义,Class来自objc_class,所以NSObject同样也有isa

  • NSObject实例化一个对象objc,因继承关系objc满足objc_object的特性(拥有isa)。isa表示指向,来源于objc_object

  • 所有的对象都是以objc_object为模板继承过来的。所以所有对象都拥有objc_object的特性。objc_object是当前的根对象

objc_object & objc_class & object & NSObject & isa

类结构分析、属性列表、方法列表探索

@interface LGPerson : NSObject
{
    NSString *oldName;
}
@property (copy, nonatomic) NSString *nickName;
-(void)sayHello;
+(void)sayNo;
@end

结构分析

struct objc_class : objc_object {
    // Class ISA;//8字节
    Class superclass;//8字节
    cache_t cache; //16字节            // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......//方法未全部贴出
}
  • isa:虽然显示注释掉了,但是其继承自objc_object,所以是有isa的。占8字节
  • superclassClass类型,由objc_object定义,是一个指针8字节
  • cache:结构体,内存大小有其内成员决定。结构体指针8字节

cache内存大小计算
此处只粘贴了需要计算的部分。(用static修饰的不存在结构体内存中)

#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    // _maskAndBuckets stores the mask shift in the low 4 bits, and
    // the buckets pointer in the remainder of the value. The mask
    // shift is the value where (0xffff >> shift) produces the correct
    // mask. This is equal to 16 - log2(cache_size).
    explicit_atomic<uintptr_t> _maskAndBuckets;
    mask_t _mask_unused;
#else
#error Unknown cache mask storage type.
#endif

#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;
  • _bucketsstruct bucket_t *类型,是结构体指针,占8字节

  • _mask的类型是mask_t,是unsigned int类型,占4字节

  • _maskAndBucketsuintptr_t,是unsigned long64位占8字节32位占4字节。使用注意所处的环境。

  • _mask_unusedmask_t类型,unsigned int类型,占4字节

  • _flags与_occupieduint16_t类型,是unsigned short,占2字节

注意:cache计算内存注意宏定义的if else并不是每一个都需要。此处使用的是64位所以其内存大小8 + 4 + 2 +2 = 16

上面我们计算好了bits前面个成员所占字节。可以通过首地址平移的方法或bits属性,查看属性及方法的存储。

bits中的数据可通过指针函数*data()获取。

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() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    ......
}

bits中存储的信息类型是class_rw_t。在其源码中会发现需要的的列表

class_rw_t中属性、方法列表
属性列表

从输出的结果中可以看到LGPerson类中bits中属性列表中有nickName属性,但是却只有1一个nickName属性,没有成员变量oldName

方法列表

方法列表中有一个c++析构函数实例方法和属性的setget方法。但是却没有类方法

通过结果发现,属性、实例方式其实是存储在当中的。

那么类方法是否存在其元类当中呢?成员变量有存储在哪里?下篇文章在探索。

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