iOS编程基础-OC(四)-内存管理


该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!


上一节介绍了消息传递和消息转发,接下来我们看看内存管理相关的内容;


 第4章 内存管理

     

     恰当的内存管理是正确而高效地开发程序的关键;

         本章详细介绍为OC程序分配和释放内存的途径、OC的内存模型,以及如何编写实现恰当内存管理的程序;

         计算机操作系统为程序分配有限内存,程序应该仅适用其必需的内存;

     

     OC语言及其运行时环境提供了支持应用内存管理的机制;

     

     

     4.1 程序的内存使用情况

     

     计算机内存中存储和使用程序的方式:

         OC可执行程序是由(可执行)代码、初始化和未初始化的程序数据、链接信息、重定位信息、局部数据和动态数据构成的;

         其中:

             程序数据包括静态方式声明的变量和程序常量(即在程序编译时在代码中设置的常数);

             可执行代码、程序数据以及链接与重定位信息会以静态方式被分配内存,并在程序的声明周期中一直存在;

             局部(自动)数据在语句中声明并且只在该语句中有效,语句执行后,局部数据不会继续存在;

             从语法上来说,OC的复合语句块就是又括号{}封装的语句集合;

     

     自动数据被存储在程序栈中,程序栈通常是在执行程序/线程前就被设定尺寸的内存段;

         栈用于存储局部变量和调用方法/函数的上下文数据;

         上下文数据包括方法的输入参数、返回值,以及调用完方法后继续执行程序的代码地址;

         操作系统会自动管理这些内存;这些数据会获得栈中的内存,而分配给这些数据的内存会在他们失效后被释放;

     

     在运行时,OC会将创建的对象(通过NSObject的alloc方法创建的)存储在动态分配的内存即堆内存中;

         以动态方式创建对象就意味着需要进行内存管理,因为在堆内存中创建的对象永远不会超过其作用范围;

     

     地位地址----------------------->高位地址

     可执行的二进制代码->程序数据->堆->未使用的内存->栈->输入程序的数据

     

     为程序代码分配的内存是在编写程序的时候设置的,因此占用的是系统内存;

     

     OC程序内存管理的必要性:

         一方面,程序的栈尺寸(通常)是在程序启动时确定的,会自动由系统管理;

         另一方面,在OC中对象是在程序执行时动态创建的,不会有系统自动回收;

     

     不进行内存管理和错误的内存管理会导致:

     1)内存泄漏:

         程序没有释放不再使用的对象,就会导致该问题;继续分配内存的话,最终会耗尽系统内存;

     2)悬挂指针:

         程序释放了仍在使用的对象,会导致该问题;如果将来程序尝试访问这些对象,就会出现程序错误;

     

     

     4.2 OC的内存模型

     

     OC的内存管理是通过引用计数实现的;

         引用计数是一种通过对象的唯一引用,确定对象是否正在被使用的技术;

         如果对象的引用计数降到了0,对象就会被视为不再有用,而且运行时系统也会释放它的内存;

     

     苹果OC开发环境提供了两种内存管理机制:

         手动管理(MRR)(也就是我们通常说的MRC,C是count的意思,是相对ARC来说的);

         自动引用计数(ARC);

     

     

     4.3 手动管理

     

     手动管理(MRR):是一种建立在对象所有权概念上的内存管理机制;

         只要对象所有者还存在,对象就不会被OC运行时环境释放;

         可以通过编写代码精确的管理对象的回收利用;

     

     我们先来理解下访问和使用对象的方式,以及访问对象与对象所有权之间的差别;

     

     4.3.1 对象引用和对象所有权

     

     OC对象是通过指向OC对象内存地址的变量,以间接方式访问的;

         这种变量就是C语言中的指针;

         指针的应用范围很广,包括OC的基本数据类型与C语言的数据类型;

         但,对象指针专门用于OC对象的交互操作;

     

     对象指针实现了OC对象的访问功能;

         但是,它们本身并不能管理所有权;

         比如:

             A * a = [[A alloc] init];

             A * b = a;

         声明一个对象指针 ,指向另一个同类型的对象指针;我们虽然改变了指针的指向,但是并未设置原始对象的所有权;

             这会导致,如果a被释放了,b就指向了一个不合法的对象;

     

     要以MRR方式管理对象生命周期:即保留和释放,在编写代码时需要遵守一系列内存管理规则;

     

     4.3.2 内存管理基本原则

     

     要正确使用MRR,编写代码时必须在获取对象所有权与释放对象所有权之间进行平衡;

     因此需遵循:

     1)为创建的所有对象设置所有权:

         应使用名称以alloc、new、copy或mutableCopy开头的方法创建OC对象;

         还应通过向块发送copy消息,以动态的方式创建OC块对象;

     

     2)应使用retain方法获取对象(你尚未拥有)的所有权:

         使用NSObject的retain方法可以获得对象的所有权;

         使用retain可以获取你想要长时间使用的对象的所有权,这样做通常可以将其存储为属性值,并防止其他操作无意中释放该对象;

     

     3)当不再使用某个对象时,必须放弃其所有权:

         使用NSObject类的release和autorelease方法可以释放对象的所有权;

         使用autorelease方法可以在当前自动释放代码块的末尾,放弃对象的所有权;

         如果对象的引用计数为0,这两种方法都会对对象执行dealloc方法;

     

     4)不能放弃不归你所有的对象的所有权:

         这样做会导致过早滴释放这些对象,如果程序尝试方位已经被释放的对象,就会出错;

     

     注意,所有权是对象指针变量相对于对象来讲的,对象一直都在,获取和释放的是对象指针变量对 对象的所有权;对象的所有权(用计数表示)清0时,相应的内存空间也会被运行时系统释放;

     

     释放操作:

     1)释放内存:

         当对象的引用计数为0时,运行时系统会通过NSObject类的dealloc方法释放掉该对象使用的内存;(注意理解这句话)

         该方法还提供了放弃子类对象所有权的框架:

             -(void)dealloc{

                 [... release];

                 ...

                 [super dealloc];

             }

         你编写的类(通常都是NSObject类的子类)都应该重写dealloc方法,调用它们实例变量的release方法,然后调用它们父类的dealloc方法;

         通过这种方式可以是你编写的类,遵循类的层次结构以适当的方式放弃对象的所有权;

     

     2)通过autorelease方法延迟释放操作:

         通过NSObject类的autorelease方法可以在自动释放池代码块的末尾,调用对象中的方法;

         自动释放代码块 提供了在将来某个时间放弃对象所有权的机制,因而无须编写调用对象中release方法的具体代码,并能避免对象立刻被释放的情况;

     

         使用@autorelease指令可以定义自动释放池代码块:

             @autorelease{

                 //创建自动释放对象的代码

                 ...

             }

         应该总是将创建自动释放对象的代码放在自动释放池代码块中;

             否则他们将无法收到release消息,从而导致内存泄漏;

 

         用于创建iOS和Mac OS X应用的苹果应用框架,尤其是AppKit和UIKit,能够自动提供自动释放代码块;

         需要手动编写自动释放代码块的情况有以下几种:

         (1)你编写的程序不是以苹果UI框架为基础的,如命令行工具;

         (2)你实现的逻辑中含有创建很多临时对象的循环;

             为了降低应用占用内存的最大值,你在该循环中添加自动释放池代码块,以便在下一次迭代前处置这些对象;

         (3)你编写的应用派生出来一个或多个辅助线程;

             你必须在执行辅助线程的位置添加自己编写的自动释放池代码块,否则你的应用就会内存泄漏;

     

     举个例子:

         你可以向这样写:

         @autorelease{

             A * a = [[[A alloc] init] autorelease];

             ...

         }

     注意:

         上述对象在创建并初始化之后,会立即收到autorelease消息,这通常由一条复合语句实现;

         这种设计可以确保所有通过autorelease消息创建的对象都会在程序结束前、在自动释放代码块的末尾被释放;

     

     

     4.3.3 使用MRR

     

     示例:

         NSString * string1 = [NSString new];//new为创建的对象设置所有权

         NSString * string2 = string1;   //对象指针变量赋值

         [string2 retain];               //对象指针变量使用retain获取对象所有权

     

         AClass * a = [[AClass alloc] init];//alloc为创建的对象设置所有权

     

         [string1 release];

         [string2 release];              //对象指针变量使用release放弃对象所有权

         [a release];

     

     以上示例展示了 以动态方式创建对象时,调动初始化和释放方式的顺序;

         所有对象的创建/保留和释放消息都达到了平衡;

     

     Xcode的Product菜单的Analyze选项:该工具会分析程序,检测潜在的内存泄漏,悬挂指针等问题;(Commend + shift + B)

     当然也可以用Xcode Instrument工具进行内存使用情况的分析;



     下一节介绍自动引用计数的使用;




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值