《OC底层系列二》-对象

前言

  • 上一篇我们分析了alloc&init&New的实现,创建一个对象实际上返回了一个id类型的obj,今天让我们来剖析这个obj。

目录

目录.png

简介

今天我们来从以下3个问题入手来探究OC对象:

  • OC对象是什么
  • OC对象包含什么
  • 如何计算OC对象所占内存大小

OC对象是什么?

上一篇中我们分析alloc最终返回了一个id类型的obj。
从源码入手分析obj,objc源码中的object.mm

typedef struct objc_class *Class;
typedef struct objc_object *id;

objc.h

/// Represents an instance of a class.
// objc_object 的声明
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

objc-private.h:

// objc_object 的实现
struct objc_object {
private:
    isa_t isa;
以下是一些函数,省略
...
}

// isa_t的定义,一个联合体,值为cls或者bits
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

其中uintptr_t是一个unsigned long类型,定义如下

typedef unsigned long           uintptr_t;

objc-runtime-new.h :

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
    以下是一些函数,省略
    ...
}
  • 结合源码我们知道,OC对象是objc_object的结构体,包含了一个联合体类型的成员isaisa的值可能是cls或者bits,cls是指针类型,占8字节,bits是unsigned long类型,占8字节,由此得到isa占大小为8字节
  • 另外根据源码,我也知道了类是一个objc_class的结构体objc_class继承自objc_object,说明类也是一个对象

OC对象包含什么

我们已经知道了OC对象是一个objc_object类型的结构体,里面包含一个联合体isa,那么isa究竟是什么?

  • 关于isa_t的具体分析,我在C语言位域详解和实例分析中的isa_t位域分析里有详细介绍,大家可以参考一下,里面对**位域 **也进行了详细的介绍。
  • 总结概述,isa_t联合体有3个成员,3个成员cls\bit\匿名结构体s共同占用8字节的内存空间,8个字节空间存储着对象、类等的一些信息,通过匿名结构体里面的位域,可以获取到这些信息。

对象所占的内存空间

关于对象所占的内存空间,严格上讲是对象的指针所指向的结构体所占的内存空间。
系统提供了2个api获取有关对象内存空间的大小,我们看看如下例子:

创建Person继承NSObject:
Person.h

@interface Person : NSObject

@end

Person.m

@implementation Person

@end

我们在main.m中添加

Person *person = [Person alloc];
FHLog(@"class_getInstanceSize: is %ld ",class_getInstanceSize([person class]));
FHLog(@"malloc_size:%ld ",malloc_size((__bridge const void*)person));

log打印如下:
class_getInstanceSize: is 8
malloc_size:16

class_getInstanceSizemalloc_size究竟代表什么呢?

  • class_getInstanceSize输出的是对象实例经过内存对齐后的占用的大小,
  • malloc_size输出的是对象实际占用内存空间的大小,即系统实际分配给对象的内存空间的大小。
class_getInstanceSize

从源码分析,相关的源码如下:

size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}

// Class's ivar size rounded up to a pointer-size boundary.
// 类的实例对象的成员变量的大小
uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

// 相当于(x+7)>>3 <<3,保证>=8且按8的倍数对齐
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

#   define WORD_MASK 7UL

// May be unaligned depending on class's ivars.
// 根据类的实例变量可能会进行内存对齐处理
uint32_t unalignedInstanceSize() {
    assert(isRealized());
    return data()->ro->instanceSize;
}
  • 这里说明一下data()->ro->instanceSize,data()是objc_class的bits.data()data()->ro->instanceSize的大小的在编译的时候就已经确定了,和类中成员变量以及对齐系数有关,默认是8的倍数,具体涉及到了内存对齐的知识。关于内存对齐,请看带你深入理解iOS-内存对齐
  • 由于unalignedInstanceSize() 可能受对齐系数的影响,得到的值可能不是8的倍数,需要word_align()保证返回的值为8的倍数。
  • 我们的Person虽然没定义成员变量,但是Person继承自NSObject,实际上是一个objc_Objec类型的结构体,但是里面包含一个联合体类型的isa,isa前面我们分析,所占大小为8字节,所以输出class_getInstanceSize: is 8。

malloc_size

  • malloc_size返回的是指针所指向的内存空间所占的大小,即系统实际分配的大小。
    在第一篇我们分析alloc流程时候,在_class_createInstanceFromZone里有
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
.....
    // 1.根据extraBytes计算对象的内存空间大小
    size_t size = cls->instanceSize(extraBytes);
    ···
    // 2.根据计算的size为obj申请分配内存
    obj = (id)calloc(1, size);
.....
}

其中cls->instanceSize实现如下:

size_t instanceSize(size_t extraBytes) {
    size_t size = alignedInstanceSize() + extraBytes;
    // CF requires all objects be at least 16 bytes.
    if (size < 16) size = 16;
    return size;
}
  • extraBytes通常为0,cls->instanceSize返回值按8字节对齐,并且保证最小值为16,所以size最小为16。
  • 调用calloc函数向系统申请分配内存,calloc的对输入的size按16字节对齐方式开辟内存空间开辟大小:size+15>>4<<4,保证size>=16且为16的倍数(calloc的源码实现在libmalloc项目里,这里暂不深入libmalloc里中calloc源码实现)。
  • 由于Person只包含一个联合体isa,因此malloc_size:16。

我们为改动Person测试如下:
Person.h

@interface Person : NSObject

@property (nonatomic, assign) int age;
@property (nonatomic, assign) int height;

@end

输出:
class_getInstanceSize: is 16
malloc_size:16

Person.h

@interface Person : NSObject

@property (nonatomic, assign) int age;
@property (nonatomic, assign) int height;
@property (nonatomic, assign) bool isHealthy;

@end

输出:
class_getInstanceSize: is 24
malloc_size:32

与我们的分析一致。

  • 总结:OC中的继承自NSObject的对象所占内存空间大小,首先根据成员变量内存对齐规则,得到实例变量所占的空间大小size,然后调用calloc输入size,按16字节对齐最后得到实际开辟的内存空间的大小。

总结

  • OC对象是什么?
    答:OC对象是一个objc_object类型的结构体
  • OC对象包含什么?
    答:OC对象是一个objc_object类型的结构体,包含一个联合体类型的isa
  • OC对象所占内存大小的的计算实现?
    答:OC对象实际占用内存大小(即系统分配的内存大小),先根据对象的成员属性8字节计算得到对象成员对齐大小(alignmentSize),再把alignmentSize按照16字节对齐计算得到对象分配内存大小(allocSize)

思考

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

推荐阅读更多精彩内容