转自:http://blog.csdn.net/mydo/article/details/36473323

乖乖隆地洞,这篇文章内容可是不得了,内存管理哦!首先,这个要是搞不明白,你就等着进程莫名其妙的挂死,或是疯狂申请内存却不释放,结果被OS杀死,不管是“自杀”还是“他杀”,都不是那么好玩的哦。其次要记住这可不是windows 中的内存管理(Win32 api),也不是linux中C like的内存管理方法。这个比他们都“高级”的多啊!但是没有ruby的高级,也没有ruby的简单,如果mac编程用ruby的就好了,这不搞出一个雨燕(SWFIT)来啊!

在Xcode4.2发布前,内存管理的确是令人恐怖的主题,都把细节推给我们码农了啊。随着4.2的发布,加入了新的自动应用计数特性(Automatic Reference Counting,ARC),码农门不再脱发着思考内存管理问题鸟。

obj-c基本内存管理模型有以下3种:

1 自动垃圾收集

2 收工引用计数和自动释放池

3 自动引用计数(ARC)

我们依次来看一下吧。

【1】自动垃圾收集

存在于obj-c 2.0中,iOS里不支持垃圾收集(不知现在是否如此哦),仅os x支持。当进程运行到某个低内存的临界点时,自动开始清理垃圾,这是一个计算密集过程,可能导致进程挂起,所以不推荐使用该特性鸟。

【2】手动管理内存计数

一般的当对象创建时,初始引用计数为1,以后每当引用一次该对象需要为该对象的引用计数加1,可以给该对象发送retain消息:

[myobj retain];

当不在需要该对象时,给其发送release消息使其引用计数减1:

[myobj release];

当对象引用计数为0时,理论上该对象不会再被使用,因为没有东西引用到它,他彻底沦为“宅对象”喽。所以可以释放其内存,通过给该对象发送dealloc消息完成这个操作。大多数情况下对象会继承NSObject的dealloc方法,当然如果类覆写了dealloc方法完成自己的析构,则当仁不让的会调用新的方法。

在手工管理引用计数时,要注意F库中一些方法会隐式的增加对象的引用计数,同样一些方法会隐式减少引用计数。

引用计数为0的引用称为悬挂指针(dangling pointer)的引用。如果给已释放的对象发送release消息,则会引起过度释放对象,会导致程序崩溃。

2.1自动释放池:设想下这种情况:方法返回一个在其中alloc的对象。这时虽然该方法不再使用这个对象,但不能释放它,因为该对象会作为该方法的返回值。NSAutoreleasePool类创建的目的就是希望能解决这个问题。通过给自动释放池发送drain消息,自动释放池中的对象会被清理和释放。

通过给对象发送autorelease消息,可以将其手动添加到自动释放池维护的对象列表中:[my_obj autorelease];

并不是所有新创建的对象都会被自动添加到自动释放池中,任何以alloc,copy,mutableCopy和new为前缀的方法创建的对象都不会被自动添加到池中。在这种情况下,我们说你拥有这个对象。当你拥有一个对象时,你必须自己负责这个对象,so你需要在使用完后自己释放这些对象的内存:主动给对象发送release消息或发送autorelease将其加入到自动释放池中,下面上代码:

[objc] view plaincopyprint?
  1. #import <Foundation/Foundation.h>
  2. int main(int argc,charchar *argv[])
  3. {
  4. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  5. NSString *str0 = [[NSString alloc] init];
  6. //alloc方法不会自动加到池中,所以要手动释放
  7. [str0 release];
  8. [pool drain];
  9. return 0;
  10. }

此代码表明在代码开始位置创建自动释放池,在进程返回前,代码结束位置清理自动释放池。值得注意的是以上代码编译时不可以加 -fobjc-arc选项,也就是不能与ARC机制同时使用哦,否则编译会出错的:

apple@kissAir: objc_src$clang -fobjc-arc -framework Foundation 5.m -o 5

5.m:5:2: error: 'NSAutoreleasePool' is unavailable: not available in automatic

reference counting mode

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

^

/System/Library/Frameworks/Foundation.framework/Headers/NSAutoreleasePool.h:8:12:note:

declaration has been explicitly marked unavailable here

@interface NSAutoreleasePool : NSObject {

^

在遇到将一个对象作为方法的返回时,我们可以用明确的代码来表示将其放入自动释放池中,确保当起引用计数为0时且pool收到drain消息时能够自动释放:

//in some method:

Some_class *ret = [[Some_class alloc] init] autorelease];

或者在实际返回时再加入:

return [ret autorelease];

2.2 事件循环和内存分配:Cocoa和ios进程运行在事件循环中,类似于windows的事件循环,比如按下一个按钮事件。每当一个新事件发生时,系统会创建一个新的自动释放池,然后可能会调用你代码中的一些方法来处理该事件。当处理完事件,并从你的方法返回后,系统在等待下一个事件发生的间隙,会清理自动释放池哦。这意味着除非对象使用retain否则无法在清空自动释放池的过程中存活下来啦。下面看一个例子:

[objc] view plaincopyprint?
  1. #import <UIKit/UIKit.h>
  2. @interface myView:UIView
  3. @property (nonatomic,retain)NSMutableArray *data;
  4. @end

假设在实现内部已经用synthesize对data属性进行了同步,再假定viewDidLoad方法会在视图载入内存是被系统调用,则在该方法中有:

data = [NSMutableArray array];

可是这样有个问题,就是前面提到的F库得方法会默认创建自动释放的对象,array创建的是一个自动释放的数组,该数组会在当前事件结束后被立即释放。为了保证数组在事件循环中还能存在,可以使用下面3中方法中的任何一种:

1. data = [[NSMutableArray array] retain]; //用retain后引用不会为0,所以不会被释放掉哦

2. data = [[NSMutableArray alloc] init]; //前面说过的alloc开头的方法不会加入自动释放池中哦

3. self.data = [NSMutableArray array];

注意最后一种方法,没有直接使用实例变量data,而是通过data属性赋值的,而data属性前面使用了retain特性,所以自动释放的数组会被保持。但是不管用3种方法的哪一种,都需要覆盖dealloc方法用于在实际销毁myView对象时释放数组哦:

[objc] view plaincopyprint?
  1. -(void)dealloc{
  2. [data release];
  3. [super dealloc];
  4. }

还是要记得调用父类中的dealloc啊!在手工引用计数环境中,可以为属性添加atomic(默认)或nonatomic特性,也可以添加assign(默认),retain和copy特性。当使用设置方法为属性赋值时我们来看看assign,retain和copy 3种特性实现上的不同:

[objc] view plaincopyprint?
  1. self.property = new_value;
  2. //assign way:
  3. property = new_value;
  4. //retain way:
  5. if(property != new_value){
  6. [property release];
  7. property = [new_value retain];
  8. }
  9. //copy way:
  10. if(property != new_value){
  11. [property release];
  12. property = [new_value copy];
  13. }

最后对手工内存管理(即不使用垃圾回收或ARC特性)规则做一个总结:

1 如果需要保持一个对象不被销毁,可以使用retain,但在使用完对象后需要手动release进行释放;

2 给对象发送release不一定会销毁该对象,除非该对象的引用计数等于0,这时系统会发生dealloc消息给该对象;

3 对于使用了retain,copy,mutableCopy,alloc或new方法的任何对象,以及具有retain和copy特性的属性进行释放时,需要覆写dealloc方法;

4 如果在方法中返回一个对象(该方法不需要该对象),则可以给该对象发送autorelease消息标记这个对象延迟释放,autorelease消息不会影响到对象的引用计数;

5 当进程终止时,该进程内存中的所有对象都会被释放(貌似是废话);

6 当开发cocoa或iOS程序时,自动释放池会随着每次事件发送而创建和清空,在此情况下,如果要使自动释放池被清空后自动释放的对象还能够存在,对象需要使用retain方法。只要对象的引用计数大于发送autorelease消息的数量,就能够在池清理后生存下来。

【3】ARC

自动引用计数ARC可以避免收工引用计数的一些潜在陷阱,但原来的引用计数仍然被维护和跟踪。然而系统会检测出何时需要保持对象,合适需要释放对象,这些你都不用担心鸟。你也不必担心返回了方法内创建的对象,编译器会管理好对戏的内存,编译器会通过生成正确的代码去自动释放或保持返回的对象(对于其他对象也是类似)。我们首先要引出强变量和弱变量2个概念:

强变量:通常在ARC中所有指针变量都是强变量。将对象引用obj_new赋值给obj_old会使obj_new对象自动保持,同时旧对象obj_old会在被赋值千被释放。强变量默认会被初始化为0,无论他是实例变量、局部变量还是全局变量这都成立。我们看以下代码:

[objc] view plaincopyprint?
  1. Some_class *obj0 = [[Some_class alloc] init];
  2. Some_class *obj1 = [[Some_class alloc] init];
  3. obj0 = obj1;

当手工管理内存时上述代码会导致obj0对象的引用丢失,随后他的值被覆盖,从而产生内存泄露,即一个变量不再被引用,但又不能够释放。如果使用ARC,obj0和obj1都是强变量,前面赋值其实会是这样:

[objc] view plaincopyprint?
  1. [obj1 retain];
  2. [obj0 release];
  3. obj0 = obj1;

但你不会在代码中实际看到上述代码,编译器在后台帮你做了这些,你只要写赋值语句就行啦。

因为所有对象默认都是强变量,所以不需要先声明,但你仍然可以使用关键字__strong:

__strong Some_class *obj;

但是默认属性不是strong,而是unsafe_unretained(相当于assign),所以如果有必要你需要这样为属性声明strong特性:

@property(strong,nonatomic) Some_class *obj;

编译器会保证事件循环中通过强属性对赋值执行保持操作,从而属性对象可以存活下来。带有unsafe_unretained(相当于assign)或weak的属性不会执行这些操作。

弱变量:在一些情况下,2个对象都持有彼此的强引用时,会产生循环保持(retain cycle),这样2个对象都不可以被销毁,即使其中一个已经不再被使用了。解决这个问题可以通过在2个对象间建立弱引用的方法解决。我们可以在父对象到子对象间使用强引用,而子对象到父对象间使用弱引用,这样就没有循环保持,弱变量也不能阻止引用的对象被销毁,在这里即是子对象无法阻止父对象被销毁。此时子对象会被系统自动设置为nil,这也避免了无意给它发送消息引起崩溃的问题。因为给nil对象发送消息不会有反应哦。

可以使用__weak关键字声明一个弱变量:

__weak Some_class *obj;

或者为属性指定weak特性:

@property(weak,nonatomic) Some_class *obj;

需要注意的是,在ios4和os x 10.6中不支持弱变量,此时你仍然可以为属性使用unsafe_unretained(或assign)特性,或者将变量声明为__unsafe_unretained,然而这时当引用的对象被销毁时,变量不再被清零喽(即被置为nil)。

细心地看客或许已经注意到,本系列到目前为止几乎所有代码都会在main中又@autoreleasepool指令,该指令围住的语句块定义了自动释放池的上下文,在自动释放池块结束的时候,任何在这个上下文中创建的对象都会被自动销毁。(除非编译器在自动释放块结束后还需要保证这个对象存在)我们可以在产生大量临时对象的地方使用这一机制:

[objc] view plaincopyprint?
  1. for(i = 0;i < n;++i){
  2. @autoreleasepool{
  3. //处理大量临时变量
  4. }
  5. }

在本文开始处提到,cocoa和ios应用运行在事件循环中,为了处理新的事件,系统会创建一个新的自动释放池上下文,在事件结束的时候,自动释放池上下文已经结束,意味着自动释放对象可能被销毁。使用ARC,这些都会在“底层”发生,你无需为此担心。

ARC很好,但其与ARC之前的代码兼容性如何呢?我们说只要非ARC代码与标准的cocoa命名规则一致,都会运行良好。当ARC遇到方法调用时会检查方法名,如果名字以alloc、new、copy、mutableCopy或init开头时,它会假定这些方法返回对象的所有者给方法的调用者。(即意思为这些返回的对象需要自己手动释放?ARC不会自动释放这些对象?)除非你使用的方法不符合标准的命名规则,此时,需要使用隐性通知编译器该方法会返回对象的拥有者(以便让编译器不会自动释放返回的方法?)。最后要注意的的是,如果你试图合成属性,而属性的名字是以上面提到的特殊词开头的话,编译器会提示一些错误哦。

ARC 强变量 弱变量相关推荐

  1. 数学归纳法全讲解(第一、第二、弱、强、双变量)带示例

    双变量数学归纳法 本文简介 单变量数学归纳 induction 弱归纳法(weak induction) 强归纳法(strong induction) 例1:强归纳证明(弱归纳无法证明) 双变量数学归 ...

  2. linux快捷键 赋值,2_Shell语言———bash的快捷键、变量声明、引用变量及变量替换...

    一.初识bash Bash是一种解释型语言,但它也提供了交互式接口,支持变量,因此提供了编程环境,所以可以称bash为程序语言. 解释型语言属于弱类型语言,其特点为: 1.它不强制区分变量类型,即无论 ...

  3. mysql定义shell变量_shell 变量的定义,赋值,运算

    一.变量类型 1.1.自定义变量 1.定义变量 不能使用纯数字命名,不能使用横杠命名 变量名=变量值 [root@localhost ~]# name=erha 2.引用变量 $变量名 或者 ${变量 ...

  4. Stata:2sls 内生变量 工具变量

    一.什么是内生性? 内生性问题是解释变量与扰动项相关导致的,具体的表现形式有遗漏变量.双向因果和测量误差. 遗漏变量 遗漏变量是指可能与解释变量相关的变量,本来应该加以控制,但是没有控制.此时该变量会 ...

  5. 耦合关系从强到弱顺序_软件设计要求—“高内聚低耦合”

    耦合度 一.什么是耦合度 软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准.划分摸块的一个准则就是高内聚低耦合. 耦合度(Coupling)是对模块间关联程度的度量.耦合的强弱取决与模块间接口 ...

  6. 耦合关系从强到弱顺序_第六周作业 -内聚耦合

    1.首先说明什么是耦合度 耦合度(Coupling)是对模块间关联程度的度量.耦合的强弱取决与模块间接口的复杂性.调用模块的方式以及通过界面传送数据的多少.模块间的耦合度是指模块之间的依赖关系,包括控 ...

  7. 耦合关系从强到弱顺序_低耦合 高内聚

    一 什么是低耦合 耦合度(Coupling)是对模块间关联程度的度量.耦合的强弱取决与模块间接口的复杂性.调用模块的方式以及通过界面传送数据的多少. 模块间的耦合度是指模块之间的依赖关系,包括控制关系 ...

  8. 编译型语言和解释型语言【附 动(静)态语言 、动(静)态类型语言、强(弱)类型语言】

    编译型语言和解释型语言篇章 1.前言 1.1 高级语言分类 计算机高级语言按 程序的执行方式 可分为:编译型和解释型. #mermaid-svg-WFWRNjQdLlIZq9HE {font-fami ...

  9. ext如何将值存入变量_变量类型之值类型与引用类型

    前言 变量类型在我们日常开发中经常接触到,但是js中的变量类型与其他强类型语言不同,由于js是弱类型语言,因此他的变量拷贝在我们实际的日常开发中有很多需要注意的项.而半斤在最近的开发中遇到了很多匪夷所 ...

最新文章

  1. 编译《OpenGL ES 3.0 编程指南》书中代码
  2. TIMING_02 浅谈时序约束与时序分析
  3. bzoj 3585 mex
  4. Orchard使用中的坎坎坷坷
  5. centos 下 sphinx安装和配置
  6. linux ssh ip地址命令,关于Linux:在ssh会话中查找客户机的IP地址
  7. python在电脑下载-Windows下下载及安装numpy、pandas及简单应用
  8. 一步一步写一个简单通用的makefile(一)
  9. 遭遇 kapjazy.dll,yhpri.dll,WinSys64.Sys,nwiztlbu.exe,myplayer.com 等1
  10. 【3dmax千千问】初学3dmax插件神器第19课3dmax学习教程|疯狂模渲大师怎么使用Mental Ray渲染器?如何通过Mental Ray渲染效果图的出图表现?
  11. 超级记忆/图像数字记忆 110位数字图像转换表 61-70
  12. 乱世王者服务器维护,乱世王者微信541区风平浪静开服时间表_乱世王者新区开服预告_第一手游网手游开服表...
  13. 日常使用计算机如何进行病毒防范,电脑日常生活中怎么防范电脑病毒
  14. aimesh r6400 开_r6900+r6300v2 aimesh 连接成功!点滴分享
  15. 小学计算机课后作业,“互联网+”背景下小学信息技术课后作业设计研究
  16. ABBYY FineReader 14
  17. 如何学习openmv?大白话openmv学习杂谈(一) For纯新手
  18. 蓝牙BQB认证 Profile测试
  19. EDUCODER---计算机硬件基础---计算机系统测试 5.16.17.19.1 合集
  20. Django 多表操作

热门文章

  1. MathType批量修改公式字体和大小
  2. 微信隐藏功能盘点:修复聊天记录
  3. 装了mysql电脑黑屏怎么办_电脑黑屏的原因,教你解决黑屏
  4. 电脑分区了如何恢复?电脑新手村教程
  5. 2021年1月国产数据库排行榜:OceanBase重回前三,TDSQL增长趋势最强劲!
  6. [CodeChef FEB15]Payton numbers(CUSTPRIM)解题报告
  7. opencv读取视频有延迟解决方法
  8. outlook 签名_Outlook 2007中的电子邮件签名礼节-适当的Flair
  9. 【转载】Unity3D导入图片属性信息和默认shader介绍
  10. 微型计算机cpu组成部分是,微型计算机的中央处理器CPU由()和()两部分组成。 - 问答库...