前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊Objective-C内存管理

聊聊Objective-C内存管理

作者头像
赵哥窟
发布2018-09-13 11:51:31
4380
发布2018-09-13 11:51:31
举报
文章被收录于专栏:日常技术分享日常技术分享

内存管理的文章网上太多了,本文只是简单的聊聊内存管理 让你加深内存管理的理解。 了解内存管理首先你需要思考几个问题 1.为什么需要进行内存管理? 2.内管管理的范围? 3.内存管理的原则? 4.对象什么时候会被释放? 5.怎么对象已经没有被引用了? 6.如何判断对象已经释放了? 7.谈谈ARC

好了思考完了下面我们来一一解答一下,如果有说的不对请指正。 1.为什么需要进行内存管理? 因为移动设备的内存极其有限,当一个程序所占内存达到一定值时, 系统会发出内存警告. 当程序达到更大的值时, 程序会闪退, 影响用户体验. 为了保证程序的运行流畅, 必须进行内存管理

2.内管管理的范围? 要回答这个问题先要了解一下内存的几大区域,这里简单的介绍一下 1、栈区(stack) — 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。 2、堆区(heap) — 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 3、全局区(静态区)(static)— 全局变量和静态变量的存储是放在一块的,初始化的 全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域, 程序结束后由系统释放。 4、常量区 — 常量字符串就是放在这里的。 程序结束后由系统释放。 5、程序代码区 — 存放函数体的二进制代码。

OC对象是放在堆里,所以由程序员分配和释放。所以管理的范围也就是OC对象

3.内存管理的原则? 内存管理的原则是:谁创建,谁释放;谁引用,谁管理。即当你使用new、alloc、copy或者mutablecopy创建对象的时候,该对象的引用计数为初始化值1,当你使用retain向对象发送消息时,你将拥有该对象的控制权,并且引用计数为+1,当你不在使用该对象的时候,你需要release或者autorelease释放该对象,使之引用计数-1,当引用计数为0时,该对象将被释放。

4.对象什么时候会被释放? 当对象没有被任何变量引用(也可以说是没有指针指向该对象)的时候,就会被释放。

5.怎么对象已经没有被引用了? 那怎么知道对象已经没有被引用了呢? OC采用引用计数(reference counting)的技术来进行管理: 1)每个对象都有一个关联的整数,称为引用计数器 2)当代码需要使用该对象时,则将对象的引用计数+1 3)当代码结束使用该对象时,则将对象的引用计数-1 4)当引用计数的值变为0时,表示对象没有被任何代码使用,此时对象将被释放。

6.如何判断对象已经释放了? 因为现在使用ARC,所以不能通过引用计数来判断对象释放了。 那怎么办?答案就是可以使用NSHashTable NSHashTable有一个特性:NSHashTable 可以持有元素的弱引用,而且在对象被销毁后能正确地将其移除。

代码语言:javascript
复制
NSHashTable *hashTable = [NSHashTable weakObjectsHashTable];
NSObject *obj = [[NSObject alloc] init];
[hashTable addObject:obj];

7.谈谈ARC 需要思考 1.ARC是什么? ARC的全称Auto Reference Counting. 也就是自动引用计数。ARC是编译器通过对代码的静态分析,确定对象的生命周期,并在合适的位置自动加上 retain 和release/autorelease的机制

2.ARC原理? 简单来说ARC的规则就是只要对象没有强指针引用,就会被释放掉,换而言之 只要还有一个强引用指针变量指向对象,那么这个对象就会存在内存中。弱指针指向的对象,会被自动变成空指针(nil指针),从而不会引发野指针错误。

3.ARC是怎么实现的? 对于这个问题首先我们需要了解Autorelease实现原理 Autorelease原理: autorelease实际上只是把对像release的调用延迟了,对于每一个autorelease,系统只是把该Object放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。

Autorelease又是怎么实现的? 答案就是AutoreleasePoolPage

ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
        {
            void * atautoreleasepoolobj = objc_autoreleasePoolPush();
            
            // do whatever you want
            
            objc_autoreleasePoolPop(atautoreleasepoolobj);
        }
        return 0;
    }

void *objc_autoreleasePoolPush(void) {
        return AutoreleasePoolPage::push();
    }
    
void objc_autoreleasePoolPop(void *ctxt) {
        AutoreleasePoolPage::pop(ctxt);
    }

而这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在于这个类。

AutoreleasePoolPage 的结构

代码语言:javascript
复制
class AutoreleasePoolPage {
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    };

magic 用于对当前 AutoreleasePoolPage 完整性的校验 上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置 AutoreleasePool是和线程一一对应的(结构中的thread指针指向当前线程) AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合(分别对应结构中的parent指针和child指针)并且每一个 AutoreleasePoolPage 的大小都是 4096 字节(16 进制 0x1000)

图像.jpg

一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入

若当前线程中只有一个AutoreleasePoolPage对象,并记录了很多autorelease对象地址时内存如下图:

图像.jpg

图中的情况,这一页再加入一个autorelease对象就要满了(也就是next指针马上指向栈顶),这时就要执行上面说的操作,建立下一页page对象,与这一页链表连接完成后,新page的next指针被初始化在栈底(begin的位置),然后继续向栈顶添加新对象。 所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置

释放时刻

POOL_SENTINEL(哨兵对象) POOL_SENTINEL 只是 nil 的别名。#define POOL_SENTINEL nil

每当进行一次objc_autoreleasePoolPush调用时,都会把一个 POOL_SENTINEL push 到自动释放池的栈顶,并且返回这个 POOL_SENTINEL 哨兵对象。

而当方法 objc_autoreleasePoolPop 调用时,就会向自动释放池中的对象发送 release 消息,直到第一个 POOL_SENTINEL:

图像.jpg

void * atautoreleasepoolobj = objc_autoreleasePoolPush(); //返回值正是这个哨兵对象的地址 objc_autoreleasePoolPop(atautoreleasepoolobj); //哨兵对象作为入参

objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:

  1. 根据传入的哨兵对象地址找到哨兵对象所处的page
  2. 在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.03.07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 释放时刻
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档