01-OC对象的本质

探究:
1.objc_alloc流程
2.init操作
3.开辟内存

YGPerson * p1 = [YGPerson alloc];
YGPerson * p2 = [p1 init];
YGPerson * p3 = [p1 init];
/**
<YGPerson: 0x600003d14300>--0x600003d14300--0x7ffee5b5d0e8 //p1
<YGPerson: 0x600003d14300>--0x600003d14300--0x7ffee5b5d0e0 //p2
<YGPerson: 0x600003d14300>--0x600003d14300--0x7ffee5b5d0d8 //p3
*/
  1. alloc开辟内存.
  • objc_alloc流程
    1.alloc - _objc_rootAlloc- callAlloc -objc_msgSend
    2.callAlloc函数内部
    cls->ISA()->hasCustomAWZ(): 是否重写+ alloc+ allocWithZone 方法,是否执行消息转发流程。
    _objc_rootAllocWithZone_class_createInstanceFromZone
    核心函数
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{

    // 几个核心函数如下
    // 计算对象的大小
    size_t size;
    size = cls->instanceSize(extraBytes); 
   //开辟内存
    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        obj = (id)calloc(1, size);
    }
    // 初始化cls信息 obj与cls绑定
   if (!zone && fast) {
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }
    return obj;
}

流程图:


image.png
  1. init操作.
    init是一个工厂模式,作为析构函数,给子类重写使用,提供接口便于扩展
    new相当于alloc init操作 源码如下:
+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}
id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}
/**
直接将self返回,实际上就是为子类提供一个工厂方法,让子类 做自定义的初始化操作
*/
  1. p1 p2 p3三个指针指向同一块内存,三个指针地址为连续的.
  • 字节对齐及其原理
    首先计算所需内存大小
inline size_t instanceSize(size_t extraBytes) const {
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }
uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
        return data()->ro()->instanceSize;
    }
   // Class's ivar size rounded up to a pointer-size boundary. 
uint32_t alignedInstanceSize() const {
        return word_align(unalignedInstanceSize());
    }
// 重点
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}
#   define WORD_MASK 7UL

举例:
[Person alloc]时,所需内存大小sizealignedInstanceSize决定
unalignedInstanceSize是8字节,来源于Person的父类NSObject中的Class isa.
由上面源码 得知 word_align算法

(8 + 7) & ~7 即  15 & ~7 
0000 1111 (15的二进制 )
0000 0111 (7的二进制)
1111 1000 (~7的二进制)

15 & ~7 与运算如下
0000 1111
1111 1000
结果
0000 1000  转换为二进制是8
8字节对齐,取8的整数,alignedInstanceSize结果为8
但是
最终计算结果 if (size < 16) size = 16可知,size小于16时,结果取16
源码上面有体现。


  • 内存对齐的原则 补充
    1.结构体(struct)或者联合体(union)的数据成员,第一个数据成员放在offset为0的地方以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。
    2.如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储)。
    3.结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍。不足的要补齐。

  • 基本数据类型的内存大小


    基本数据类型
  • 计算机基础

十六进制数字与二进制数字的对应关系如下:
0000 -> 0、0001 -> 1、0010 -> 2、0011 -> 3、
0100 -> 4、0101 -> 5、0110 -> 6、0111 -> 7、
1000 -> 8、1001 -> 9、1010 -> A、1011 -> B、
1100 -> C、1101 -> D、1110 -> E、1111 -> F。
因此,1个16进制数对应4个二进制数位,2个16进制数位对应8个二进制数位,及1个字节

计算得出内存占用大小

struct Person1 {
    char a; // [0]
    double b; // [8 16)
    int c; // [16 20)
    short d; // [20 22)  ----->24
}MyPerson1;

struct Person2 {
    long b; // [0 8)
    char a; // [8]
    int c; // [12 16)
    short d; // [16 17]  ----->24
}MyPerson2;

struct Person3 {
    long b;//[0 8)
    int c;//[8 12)
    char a;//[12]
    short d;//[14 15] -------->16
}MyPerson3;

struct Person4 {
    double a;                  //[0,7]
    int b;                         //[8,11]
    char c;                     //[12]
    short d;                   //跳过13 [14,15]
    int e;                        // [16,19]
    struct Person1 str; //根据准则2,Person1最大元素为`double`类型,所以从24开始。根据`Person1 `分配的时候24个字节,所以str为[24,47]
} Person4;


x/nuf ------->x/4gx
n 表示要显示的内存单元的个数


u 表示一个地址单元的长度
b 表示单字节
h 表示双字节
w 表示4字节
g 表示8字节


f 表示显示方式,可取以下值:
x 按十六进制格式显示变量
d 按十进制格式显示变量
u 按十进制格式显示无符号整型
o 按八进制格式显示变量
t 按二进制格式显示变量
a 按十六进制格式显示变量
i 按指令地址格式显示变量
c 按字符格式显示变量
f 按浮点数格式显示变量

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

推荐阅读更多精彩内容