iOS 底层 - OC对象内存管理之MRC

本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !

作为一名开发者来说对内存管理一定不会感到陌生,那么内存管理到底是管理哪部分的内存呢?

iOS 程序内存布局比较了解的应该会知道内存布局分为:保留区、代码段、数据段、堆区、栈区、内核区;真正需要开发者参与管理的是:在堆空间申请的内存;如果程序员不释放,程序结束后,会由操作系统回收; 这里的结束也有可能是 崩溃导致 的结束

我多句嘴啊,为什么栈空间不需要开发者参与管理内存 ?

对啊,这是为什么呢 ? 嗯 原来是由编译器自动分配释放,所以不需要开发者参与咯 !

简单描述栈空间的内存管理原则 ?

分配:编译阶段由编译器来给参数值、局部变量等分配存储空间
释放:当超出其作用域时,由编译器控制从栈中弹出。

Manual Reference Counting :MRC, 手动引用计数

Automatic Reference Counting : ARC ,自动引用计数

  • MRC:遵循谁申请、谁添加、谁释放的原则。需要手动处理内存计数的 +1-1。从12年iOS5开始,逐步被ARC(自动引用计数)模式取代。

  • ARC取代了MRC后,在App编译阶段,由编译器(LLVM)在合适的位置添加了OC对象的内存管理代码。

OC对象的内存管理原则:

  • 在iOS中,使用引用计数来管理OC对象的内存

  • 一个新创建的OC对象引用计数默认是1,当引用计数减为0时;OC对象就会销毁,释放其占用的内存空间

  • 调用retain会让OC对象的引用计数 +1,调用release会让OC对象的引用计数 -1

内存管理的经验总结

  • 当调用allocnewcopymutableCopy方法返回了一个对象,当不需要这个对象时,就要调用release或者autorelease来释放它

  • [NSMutableArray array][NSMutableDictionary dictionary]等创建出来的对象不需要再添加内存管理代码(releaseretain),
    因为方法内部已经实现的内存管理代码

  • 想拥有某个对象,就让它的引用计数 加一; 不想再拥有时,就让它的引用计数 减一

  • MRC环境OC对象的声明:使用retain关键字修饰,在dealloc的父类方法调用前释放; 不需要实现setter方法的内存管理代码,因为用retain关键字修饰后已经自动实现了setter方法、getter方法以及内存管理代码

MRC环境OC对象的声明示例

@property (nonatomic, retain)XYHCar  *car;

- (void)dealloc
{
//    [_car release];
//    _car = nil;
//或
    self.car = nil;    
    // 父类的dealloc放到最后
    [super dealloc];
}

  • 可以通过以下私有函数来查看自动释放池的情况
void _objc_autoreleasePoolPrint(void)

这很奇怪,既然是私有函数怎么能够被外界调用呢 ?怎么做到的呢 ?
可以的,使用 extern 关键字(前提是该函数没有被struct修饰为静态方法)对该函数在需要用到的地方重新声明一遍,真正调用时编译器会去查找有没有这个函数,只要有就会调用啦 ! 比如:

示例代码

传入void表示没有参数
extern void _objc_autoreleasePoolPrint(void);

int main(int argc, const char * argv[]) {
   @autoreleasepool { //  r1 = push()
       
       XYHOlg *olg1 = [[[XYHOlg alloc] init] autorelease];
       XYHOlg *olg2 = [[[XYHOlg alloc] init] autorelease];
       
       @autoreleasepool { // r2 = push()
           for (int i = 0; i < 6; i++) {
               XYHOlg *olg3 = [[[XYHOlg alloc] init] autorelease];
               NSLog(@"%@",olg3);
           }
           @autoreleasepool { // r3 = push()
               XYHOlg *olg4 = [[[XYHOlg alloc] init] autorelease];
               _objc_autoreleasePoolPrint();
               NSLog(@"%@,%@,%@",olg1,olg2,olg4);
           } // pop(r3)
       } // pop(r2)
       
   } // pop(r1)
   
   return 0;
}

输出结果
(hot) :当前 pool page
(cold):不是当前 pool page
(hot)(cold): 就一个pool page,怎么着都行

objc[1363]: ##############
objc[1363]: AUTORELEASE POOLS for thread 0x1000d1dc0
objc[1363]: 12 releases pending.
objc[1363]: [0x100805000]  ................  PAGE  (hot) (cold)
objc[1363]: [0x100805038]  ################  POOL 0x100805038 第一个自动释放池
objc[1363]: [0x100805040]       0x10051fbc0  XYHOlg
objc[1363]: [0x100805048]       0x10051fc10  XYHOlg
objc[1363]: [0x100805050]  ################  POOL 0x100805050 第二个自动释放池
objc[1363]: [0x100805058]       0x10051fc20  XYHOlg
objc[1363]: [0x100805060]       0x10051fc30  XYHOlg
objc[1363]: [0x100805068]       0x100520130  XYHOlg
objc[1363]: [0x100805070]       0x100520140  XYHOlg
objc[1363]: [0x100805078]       0x100520150  XYHOlg
objc[1363]: [0x100805080]       0x100520160  XYHOlg
objc[1363]: [0x100805088]  ################  POOL 0x100805088 第三个自动释放池
objc[1363]: [0x100805090]       0x100520170  XYHOlg
objc[1363]: ##############
Program ended with exit code: 0

release和autorelease的区别 ?

  • release:在使用完对象后调用
  • autorelease:
    在初始化的时候调用,
    一个对象被autorelease,则该对象内存地址将被追加到当前任务的AutoreleasePool(自动释放池)里,通过AutoreleasePoolPage对象来管理,在合适的时机释放
    AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage双向链表的形式组合而成(分别对应结构中的parent指针和child指针)

ARC环境声明OC对象可以用assign来修饰吗 ?为什么?

  • 当然可以用来修饰OC对象,且编译器还不会报错,但不推荐
  • assign关键字修饰的OC对象在 setter方法不会生成 相关的内存管理代码
  • assign关键字修饰的对象释放后,指针不会被清空 依然指向释放前的对象;这很危险,在后续的操作中如果用到该对象会出现悬垂指针访问导致崩溃
  • assign: 多用来修饰基本数据类型

小小知识点:指针

注意:这里的内存管理 范围OC对象 ,至于基本数据类型如:intfloat不在范围之内;

OC对象的引用计数都是自然数吗 ?

绝大多数是的,只有NSString变NSTaggedPointerString时引用计数为其内存地址的十进制数

在开发中就没有需要用到OC对象之外的数据类型了吗 ?他们的内存管理该如何实现 ?

在iOS开发过程中可能会用到的语言有很多,比如:CC++RNSwiftJavascript等;这其中最多使用到是C的一些数组、字符串、指针, C语言中对堆内存的申请是调用malloc函数实现、释放是调用free函数实现;
Javascript 具有自动垃圾回收机制,执行环境会管理代码执行过程中使用的内存。

示例代码:

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

推荐阅读更多精彩内容