iOS 内存管理

iOS 的内存管理分为 MRC 跟 ARC

一 、 为什么要管理内存

app 所占的内存比较多的时候,系统会发出内存警告
这时得回收一些不需要再使用的内存空间。
比如一些不需要使用的对象、变量等、

  • 内存管理的范围:
    任何继承了NSObject的对象
    对其他数据类型如:
    int char float double struct enum 等 不需要管理内存

  • 内存的结构
    🍎 内存分为五大区域
    其中的两大区域
    🍇 栈区
    🍇 堆区

🍇 栈区
存放一些局部变量
例如:int a = 10;

  int a = 10;
  int b = 20;
  Person *p;       // Person类型的指针p

🍇 堆区
存放动态产生的数据 (alloc) 出来的数据
例如:Person对象

[Person alloc];

在OC中 此区域的对象在没有变量指针指向它的时候
它也不会自动销毁或者是自动释放
需要使用本对象调用回收机制的方法来释放这个对象
在Java中不一样,当没有指针指向对象的时候,垃圾回收机制会自动回收这种对象。


🍒 数据区
🍇 存放一些静态变量和字符串常量
🍒 代码区
🍇 存放代码

二 、引用计数器的基本概念

🍎 对象的基本结构
每个对象内部都会特意分配4个字节的空间,存放一个整型的引用计数器
用来计算 “对象被引用的次数”
也就是说有多少个指针在使用这个对象。


🍎 引用计数器的作用
🍇 当使用 alloc new copy 创建一个对象的时候,这个对象内部的引用计数器的值默认就是 1
🍇 当一个对象的引用计数器的值为 0 的时候
它所占用的内存就会被系统回收掉
换句话说
如果对象计数器不为 0 的时候
那么在整个程序的运行过程中 所占的内存
就不肯能被回收,除非整个程序已经退出。

三 、 引用计数器的用法

  1. 方法的基本使用
    1> retain :计数器+1,会返回对象本身
    2> release :计数器-1,没有返回值
    3> retainCount :获取当前的计数器的值
    4> dealloc
 * 当一个对象要被回收的时候,就会调用 dealloc
 * 但要调用[super dealloc],这句调用要放在最后面
 * ARC中虽然可以重写 dealloc 方法、但不用调 [super dealloc] 而且也不可以调用

/

  1. 概念
    1> 僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
    2> 野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
    3> 空指针 :没有指向任何东西的指针(指针存储的东西是nil、NULL、0) OC 不存在空指针错误,给空指针发送消息不会报错
[object retain];  // 使引用计数器的值 +1 (调用 retain 方法会返回对象的本身)
[object release]; // 引用计数器的值 -1 (调用 release 无返回值)
[object retainCount]; // 给对象发送一条 retainCount 消息 可以获取对象当前计数器的值

野指针错误 / 经典错误

在OC中指向僵尸对象 或指向(不可用内存)的指针 称为野指针
在OC中给野指针发送消息会报错 EXC_BAD_ACCESS 访问了一块坏的内存(已经被回收的、不可以用的内存)
OC中不存在空指针错误,给空指针发送消息,不报错
故操纵野指针是非常危险的事情。
强行访问一块不可用内存。程序出错(绿色)
EXC_BAD_ACCESS 访问了一块坏的内存(已经被回收的、不可以用的内存)
EXC_BAD_ACCESS(code=1,address=0x20)

🍎 给已经挂掉的僵尸对象发送一条 [对象名 retain] 消息时
🍇 会出现两种情况

  1. 在开启僵尸对象检测的时候 则程序崩溃。
  2. 未开启动僵尸对象检测时候 则不会起到任何作用

🍎 开启僵尸对象检测功能时

🍇当给已经释放的对象发送消息是会导致程序崩溃(程序闪退、闪退..)
    例如:
     Person *p = [Person alloc] init];
     [p release];
     [p setName:@"张三"];
 则会报错:
  - [Person setName:]:message sent to deallocted instance 0x100109a10
 报错原因是:给已经释放的对象发送了一条  - [setName:]  的消息

🍎 没有开启僵尸对象的检测功能

🍇当给已经释放的对象发送消息是会导致程序崩溃
  则不会警告、不会报错、也不会崩溃、
    例如:
     Person *p = [Person alloc] init];
     [p release];
     [p setName:@"张三"];
  在程序运行完毕,没有出现报错,只是setName方法赋值没有成功。
  所以,这个消息只是一行空代码而已,没起到任何作用。

property 的内存管理

1.内存管理相关的参数
    
    1> @property (retain) release 一次旧对象  retain 一次新对象 (适用于OC对象类型)
    2> @property (assign) 直接赋值 ( 默认的 只适用于非OC对象类型 )
    3> @property (copy) release 一次旧对象 拷贝一个新对象出来

2.是否要生成 set 方法
    1> @property (readonly)  (默认)只会生成 getter 方法的声明和实现
    2> @property (readwrite) 同时生成 setter 和 getter 方法的声明和实现

3.多线程管理
    1> @property (nonatomic) 性能高
    2> @property (atomic)    性能低 (默认)

4.setter 和 getter 方法的名称
    1> @property (setter = methd) 决定了set方法的名称 一定要有个冒号 :
    2> @property (getter = methd) 决定了get方法的名称 (一般用在BOOL类型)
    此方法一般用于 BOOL 类型   
    例如:
        @property (getter = isRich) BOOL rich ;

MRC 内存管理代码规律以及规范

 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
 2.你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
 3.谁alloc,谁release
 4.谁retain,谁release
  1. 只要调用了 alloc,必须有 release(autorelease)
    对象不是通过 alloc 产生的,就不需要 release

  2. setter 方法的代码规范
    1> 基本数据类型:直接复制

 - (void)setAge:(int)age
 { 
    _age = age;
 }

2> OC对象类型

 - (void)setCar:(Car *)car
 {
    // 1.先判断是不是新传进来对象
    if ( car != _car )
    {
        // 2. 如果不是同一个对象,对旧对象做一次release
        [_car release];

        // 3.对新对象做一次retain
        _car = [car retain];
    }
 }
  • dealloc 方法的代码规范
    1> 一定要[super dealloc],而且放到最后面
    2> 对self (当前对象) 所拥有的其他对象做一次release
- (void)dealloc 
{
    [_name release]; // person 拥有的 _name 对象
    [_book release];  // person 拥有的 _book 对象
    [super dealloc];
}

四、autorelease

1.系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都是已经autorelease过的

例如:

[NSDate date];
[NSString stringWithFormat:....];

2.开发中经常写一些类方法快速创建一个autorelease的对象

  • 创建对象的时候不要直接使用类名,用self
    例如 Person 类
+ (instancetype)person
{
  return [[self alloc] init] autorelease];
}

ARC

  • 指针分2种

<1> 强指针: 默认情况下,所有的指针都是强指针 __strong
<2> 弱指针: __weak 当没有强指针指向对象的时候弱指针指向的对象就会被回收掉

  • ARC的判断准则: 只要没有强指针指向对象,对象就会被ARC机制释放掉

<1> 不允许调用 releaseretainretainCount
<2> 允许重写 dealloc 但是不允许调用 [super dealloc];
<3> @property 的参数
(strong) 意味着成员变量是个强指针 / 相当于原来的 retain (适用于任何 OC类型)
(weak) 意味着成员变量是个弱指针 / 相当于原来的 assign (适用于任何 OC类型)
(assig) 适用于非 OC对象类型
<4> MRC 时用的 retain 全部改为 strong

  • 关于对象循环引用的问题

当两端循环引用的时候,解决方案:
.h 中使用 @class ClassName;
ARC
一端用strong,另一端用weak
MRC
一端用retain 一端用assgin

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

推荐阅读更多精彩内容