A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Simpon 中级黑马   /  2016-5-24 09:50  /  3794 人查看  /  4 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 Simpon 于 2016-10-19 11:43 编辑

浅谈MRC内存管理

第一节.  为何要进行内存管理?
因为iphone手机本身内存较小,在运行繁多的app程序的时候,会占用大量的内存,如果内存不敷使用的时候,我们就需要去释放一些已经不再需要使用的内存空间,保证app可以正常运行使用。

目前主流的iphone手机内存最大可达到2GB。如iphone6s,iphone se都只有2GB的内存,当app占用的内存超过40M的时候,iOS将会发出内存警告,当app占用的内存超过45M的时候,将会发出二次警告,当app占用的内存超过120M的时候,iOS将会闪退该程序,所以,我们需要对app占用的内存进行管理,保证app运行流畅,不被强制闪退。



第二节.  什么是内存管理?
当我们编写程序时,会声明各种各样的变量,编写各种各样的代码,而他们都会占用内存。
但是,并不是所有的变量和占用的内存都由我们来进行释放。
下面我们来了解一下,内存的五大区域:
栈:存放的是局部变量。当局部变量所在的作用域结束的时候就会由系统立即回收局部变量所使用的内存空间。
堆:存放的是由程序员手动申请的变量。手动申请的变量可以由程序员手动编写代码来进行回收。
bss段:存放的是未初始化的全局变量和静态变量。当全局变量和局部变量初始化的时候,系统将会从BSS段回收空间,并将他们存放到数据段。
数据段:存放的是已经初始化好的全局变量和静态变量,常量。在程序结束的时候由系统立即回收他们所占用的空间。
代码段:存放的是代码。在程序结束的时候由系统立即回收。

当我们编写程序的时候,将会根据语法的不同,类型的特性等等,在上面的五个区域中开辟空间,存储数据。
其中,在栈,bss, 数据段, 代码段中创建和占用的空间,由系统进行内存的回收,也就是说,这部分内容不需要我们进行管理。

那么,重点来了,我们需要管理的其实就是内存中的堆区域。
在堆中占用的空间必须通过我们程序员的代码来进行回收,如果没有及时的进行内存回收,就会造成内存的浪费,内存泄露等情况,直至app被闪退。

所以,我们需要将堆区域中占用的已经使用完毕的空间进行回收。
具体下来,其实就是要回收我们在堆空间创建的变量,对象。




第三节.  如何进行内存管理
当我们使用OC语言编写程序的时候,最常见的就是各种各样的对象。而OC对象就是被存储在堆空间中,当我们创建一个对象的时候,该对象就会被创建在堆空间中,比如:
  1.        Person*jack = [[Person alloc]init];
  2.        Person*rose = [Person new];
复制代码

而这种创建的对象都会被创建在堆空间中,如果不进行内存管理,这些对象将会一直占用内存空间,直接程序结束或者程序闪退。

在MRC(Mannul Reference Counting,手动引用计数)机制下,每一个对象都有一块4个字节的存储空间(一个整数)记录对象被引用的数量,也就是说有一块内存空间记录了一个对象正在被多少东西使用,而这块内存空间被称为引用计数器。

当一个对象的引用计数器为0的时候,对象将会被立即回收。
一个对象被创建出来以后,默认引用计数为1.

所以我们可以通过对引用计数器的操作来进行对象的回收,并完成内存管理的操作。


第四节.  操纵对象的引用计数器
我们可以向对象发送一些消息来操纵对象的引用计数器,如下:

1.    retain消息:发送retain消息可以为对象添加1个引用计数,如下:
  1. Person *jack = [[Person alloc]init];//此时jack指针指向的对象引用计数为1
  2. [jack retain];//发送retain消息,此时jack指针指向的对象引用计数为2
复制代码

2.    release消息:发送release消息可以为对象减去1个引用计数,如下:
  1. Person *rose = [[Person alloc]init];//此时rose指针指向的对象引用计数为1
  2. [rose release];//发送release消息,此时rose指针指向的对象引用计数为0
复制代码

注意,当一个对象的引用计数为0 的时候,它就会被立即回收.所以上面的rose指针指向的对象已经被回收了,可喜可贺。

其他消息:retainCount消息,当我们向对象发送retainCount消息的时候,将会返回对象的引用计数器值,所以,想要知道对象有多少使用者,可以通过retainCount消息来获得。如下:
  1. Person *king = [[Person alloc]init];//此时king指针指向的对象引用计数为1
  2. NSUIntegeruser = [king retainCount];//此时user变量将会获得king的引用计数
复制代码

第五节.  对象的dealloc方法
当对象的引用计数为0的时候,将会调用对象的dealloc方法,然后再回收这个对象所占用的内存空间,所以,如果我们想要监视一个对象是否被回收,我们可以重写对象的dealloc方法,如下例:

Person.h文件:
  1. #import <Foundation/Foundation.h>
  2. @interface Person : NSObject
  3. {
  4.    int _age;//年龄
  5. }
  6. //getter,setter方法
  7. -(void)setAge:(int)age;
  8. -(int)age;
  9. //普通方法
  10. -(void)sayHi;
  11. @end
复制代码

Person.m文件:
  1. #import "Person.h"
  2. @implementation Person
  3. //重写dealloc方法来监视对象有没有被回收
  4. -(void)dealloc
  5. {
  6.        //当这句话在控制台上打印的时候,人类对象就被回收了。
  7.     NSLog(@"人类对象已经被回收了");
  8.    [super dealloc];
  9. }
  10. -(void)setAge:(int)age
  11. {
  12.    _age = age;
  13. }
  14. -(int)age
  15. {
  16.    return  _age;
  17. }
  18. -(void)sayHi
  19. {
  20.    NSLog(@"你好");
  21. }
  22. @end
复制代码

注意:一般我们都会重写dealloc方法来监视对象的回收,这样才能更好的将对象进行内存管理。


第六节:内存管理的原则

讲解到这里,我们就会发现,其实对象的内存管理挺容易的,完全就是要靠引用计数器来实现管理,而引用计数器可以通过代码来进行操纵。

所以,什么时候为对象发送retain消息,什么时候为对象发送release消息成为了内存管理中的重中之重。

那么,什么时候为对象发送retain消息呢,什么时候为对象发送release消息呢?

这就是内存管理的原则,具体原则内容如下:
A.        当对象被创建出来以后,对象的引用计数默认为1,所以在这个对象使用完毕以后我们应该为这个对象发送一条release消息,保证这个对象在使用完毕以后引用计数变为0,并且占用的内存空间被回收。
B.        当对象被别人使用的时候,别人就会为这个对象发送retain消息,表示使用的人多了一个,当别人不再使用对象的时候,别人就会为对象发送release消息,表示使用的人少了一个。
C.        当对象还有人正在使用的时候,对象就不应该被回收(引用计数器不为0)
D.       谁发送了retain消息,当它使用完毕之后,谁就应该发送release消息

以上就是MRC内存管理的基本原理,下节我们将要了解的是MRC内存管理的相关概念。


精华推荐:

4 个回复

倒序浏览
挺全面的,谢谢分享!
回复 使用道具 举报
完全看不懂啊
回复 使用道具 举报
很喜欢这种手动释放内存的感觉,爽...
回复 使用道具 举报
xiaoxin1789 发表于 2016-5-24 23:02
很喜欢这种手动释放内存的感觉,爽...

爽太多总会累的
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马