内存管理2

我们讨论过properties 后,所有的内存管理系统都是通过控制所有对象的生命周期来减少内存的占用。iOS和OS X应用程序完成这些是通过对象拥有者来实现的,它保证了只要对象使用就会存在,但是不长。

这种对象拥有者的模式来自于引用计数系统,它会记录对象现在被多少对象拥有,当你生命一个对象的拥有者,你要增加它的计数,而当你不用这个对象的时候,你需要减少这个计数。只要它的引用计数大于0,对象就一定会存在。但是一旦计数为0,操作系统就会被允许释放它。

在过去,开发者通常通过调用一个被NSObject protocol定义的特殊的内存管理方法来控制对象的引用计数。这个方法叫做Manual Retain Release(MRR),也就是手动保持释放。然而,到了Xcode4.2之后介绍了Automatic Reference Countin(ARC),就是自动引用释放。ARC自动地插入了所有他们的方法。

如今的应用程序应该总是使用ARC,

因为它更加可靠而且使你专注于你的App特性而不是内存管理。

该文章主要解释引用计数概念里面的MRR,然后讨论一些特别需要关注的关于ARC的一些知识。

Manual Retain Release

在手动保持释放环境中,持有和放弃每个对象的所有权是我们的工作。你实现这些需要调用一些特殊的内存相关方法,下面就是用到的方法以及简短描述

方法 行为
alloc 创建一个对象并且声明它的所有权
retian 声明一个存在对象的所有权
copy 复制一个对象,然后声明它的所有权
release 放弃一个对象的所有权,然后立刻销毁它
autorelease 放弃对象的所有权,但是延迟销毁。(最终也是销毁)

手动的控制一个对象的所有权貌似看起来是一件令人害怕的任务,但是其实它非常容易。你要做的就是声明任何你需要对象的所有权,当你不再使用的时候记得放弃所有权就行了。从实用的角度来看,意味着你必须平衡alloc,retain和copy的调用使用release或者autorelease再相同的对象上。

如果你忘记了release一个对象,那么它的潜在的内存将不会被释放,这样就会造成内存泄露。如果泄露严重就会导致程序崩溃。相反如果你调用release太多次数的话,会产生野指针(悬挂指针)。当你试图访问悬挂指针的时候,你就会请求一个无效的内存地址,这样你的程序很有可能崩溃。

下面来解释一样如何通过合理的使用上面提到的方法来避免内存泄露和悬挂指针。

允许MRR

内存管理(1)中我们介绍过了,就不在介绍了。

alloc方法

我们已经知道使用alloc创建一个对象。但是,它不仅仅是给对象分配了内存,它也设置它的引用计数为1.这也合理,因为我们如果不想持有这个对象(持有一小会儿也算)那么我们就没有必要去创建对象。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:@"Scott"];
NSLog(@"%@",mutableArray);
}
return 0;
}

上述带么我们肯定很熟悉,就是实例化一个可变数组,添加一个value,然后打印它的内容。从内存管理的角度来看,我们现在有一个mutableArr对象,这就意味着后面我们必须在某个地方要release它。

但是,在代码中我们没有释放它,我们的代码现在就有一个内存泄露。我们可以通过Product-->Analyze测试出来。结果如下:

这就有一个问题,它可以在上图中看到(main.m)。

这是一个很小的对象,因此泄露不太致命。然而,如果他一次又一次地发生(例如在一个循环中或者用户一直点击一个按钮),那么这个程序就会最终用完内存,然后崩溃。

release方法

该方法通过放弃对象的所有权来减少引用计数。因此,我们可以解决内存泄露问题通过在NSLog()后面添加下面的代码:

[mutableArray release];

现在我们的alloc和release就平衡了,静态分析就不会报出任何错误。典型的,你将会想放弃对象的所有权在同样的方法之后。

过早释放会造成悬挂指针。例如,在NSLog()前面去调用release方法。因为release会立刻释放占用的内存,mutableArray变量在NSLog()里面就指向了一块不可用的内存,然后你的程序就会崩溃:EXC_BAD_ACCESS error code 当你想运行的时候。(最新的Xcode现在直接打印出来空,而没有提示错误。)

因此,不要在还在使用一个对象的时候而去释放它。

retain方法

我们现在新建一个对象CarStore。

CarStore.h

#import <Foundation/Foundation.h>

@interface CarStore : NSObject

- (NSMutableArray *)inventArr;

- (void)setInventArr:(NSMutableArray *)newInventArr;
@end

CarStore.m

#import "CarStore.h"

@implementation CarStore
{
NSMutableArray *_inventArr;
}
- (NSMutableArray *)inventArr {
return _inventArr;
}
- (void)setInventArr:(NSMutableArray *)newInventArr {
_inventArr = newInventArr;
}
@end

然后我们在main.m中进行如下操作:

#import <Foundation/Foundation.h>
#import "CarStore.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:@"Scott"]; CarStore *superStore = [[CarStore alloc] init];
[superStore setInventArr:mutableArray]; [mutableArray release];
NSLog(@"%@",[superStore inventArr]); }
return 0;
}

此时我们会发现里面是没有数据的。因为此时inventArr就是一个悬挂指针,因为对象已经被released了在main.m中。现在,superstore对象有个弱引用array.为了实现强引用,CarStore需要声明数组所有权:

- (void)setInventArr:(NSMutableArray *)newInventArr {
_inventArr = [newInventArr retain];
}

这样就确保了inventArr没有被释放当superstore正在使用他的时候。可以看一下:retain方法返回是对象的本身,这就是我们执行retain和指派任务在一行。

然而这又造成了另一个问题:retain调用没有平衡release,因此会产生另一个内存泄露。当我们传递过数组之后,我们不能访问老得数值,这就意味着我们将不会使用它,为了解决这个问题,我们需要调用release去释放老值: //不太理解,依然会报内存泄露

- (void)setInventArr:(NSMutableArray *)newInventArr {
if (_inventArr==newInventArr) {
return;
}
NSMutableArray *oldValue = _inventArr;
_inventArr = [newInventArr retain];
[oldValue release];
}

这就是tetain和strong属性做的事情,使用property将会更方便。

由此可知alloc和retain必须和release平衡,确保内存最终被释放。

copy方法

另一种保留是复制的方法,它创建了一个新实列对象和增加了引用计数,保留最初的影响。因此,如果你想复制mutalbeArr,而不是指向可变的,你可以修改setInventory方法如下:

- (void)setInventArr:(NSMutableArray *)newInventArr {
if (_inventArr==newInventArr) {
return;
}
NSMutableArray *oldValue = _inventArr;
_inventArr = [newInventArr copy];
[oldValue release];
}

一些类支持多重复制方法(例如多重init方法)。任何copy的对象具有相同的行为。

autorelease方法

就像release一样,autorelease方法放弃对象的所有权,但是不是立刻销毁对象,它延迟释放内存。这样允许你当你应该释放对象的时候释放。

例如,考虑一下一个简单的工厂方法:

	+ (CarStore *)carStore;

从技术上讲,是carStore方法对对象的释放负责,因为调用者不知道该方法拥有返回对象。因此,它应该实现返回一个自动释放的对象,就像下边:

+ (CarStore *)carStore {
CarStore *newStore = [[CarStore alloc] init];
return [newStore autorelease];
}

那么这个对象就放弃了所有权,延迟释放,直到在最近的@autoreleasepool{}块中,然后会调用正常的release方法。这就是为什么main()函数被@autoreleasepool包围着:

它确保当程序结束运行的时候,自动释放对象被销毁。

在ARC之前,它是一个很方便的方式,因为它让你创建对象但是不用担心在后面什么时候哪里去调用release。

如果你改变superstore的创建方式,使用下边的:

//        CarStore *superStore = [[CarStore alloc] init];
CarStore *superStore = [CarStore carStore];

实际上,你不允许释放这个superstore实例了现在,因为你不再拥有它--而是carStore工程方法拥有它。一定不要去release一个autorelease对象,否则你会产生悬挂指针,甚至使程序崩溃。

dealloc方法

该方法和init方法相同。它被合理的使用在对象销毁之前。给你个机会来清理任何内部的对象们。这个方法通过runtime自动调用------

你不应该去自己调用dealloc。

在MRR环境中,最常见的你需要做的就是使用dealloc方法去释放一个实例变量存储的对象。想想我们刚才的CarStore当一个实例dealloc时发生了什么:它的_inventArr(被setter保持着的)从来没有机会被释放。这是另一种形式的内存泄露,为了解决这些,我们要做的就是添加一个dealloc方法到CarStore.m中:

- (void)dealloc {
[_inventArr release];
[super dealloc];
}

你必须要调用super dealloc去保证父类中的实例变量在合适的时间去释放。为了让dealloc简单,我们不应该在dealloc里面去进行逻辑处理。

MRR总结:

总之,关键是处理好alloc,retain和copay 和release活着autorelease之间的平衡,否则你就会遇到悬挂指针活着内存泄露在你的程序中。

在真实地路上,上面好多代码都是废弃的。但是通过上面可以让你更好的理解ARC编译原理。

Automatic Reference Counting(自动引用计数,ARC)

现在,你已经了解了MMR,现在你可以忘记他们了。ARC和MRR的工作方式一样,但是它自动为你插入合适的内存管理方法。这对于OC开发者是很好地处理。因为我们可以把精力放在应用程序需要做什么而不是如何做。

ARC中人为错误的内存管理几乎不存在,所以使用他的唯一理由可能就是使用过去的第三方代码库。(然而,ARC大部分情况下向后兼容MRR程序)。下面就是介绍MRR和ARC之间的切换。

允许ARC

Project--->Build Settings--->搜索garbage,找到Objective-C Automatic Reference Counting设置为YES即可。

没有更多的内存方法

ARC通过分析代码每个对象的理想的生存时间应该是多来工作,然后自动的插入必要的retain和release方法。该方式需要完全控制整个程序中对象的所有权,

这就意味着你不允许调用retain,release或者autorelease

唯一你可能见到的在ARC中的内存相关方法就是alloc和copy。

新的属性

ARC介绍了新的@property属性,你应该使用strong来代替retain,使用weak来代替assign。这些已经在properties 讨论过了。

ARC中的dealloc方法

dealloc在ARC中有一点不同,你不要release实例变量在dealloc方法中---ARC已经为你实现了。另外,父类的dealloc是自动调用,你也不需要[super dealloc]

但是有一种例外,如果你使用的低层次的内存构造函数,就像malloc(),那样的话,你仍然需要调用free在dealloc中去避免内存泄露。

总结

ARC中你唯一要关系的就是循环引用。

你应该明白了所有你应该知道的关于OC的内存管理。

附:

内存管理2(主讲MRR)的更多相关文章

  1. iOS夯实:内存管理

    iOS夯实:内存管理 文章转自 内存管理 最近的学习计划是将iOS的机制原理好好重新打磨学习一下,总结和加入自己的思考. 有不正确的地方,多多指正. 目录: 基本信息 旧时代的细节 新时代 基本信息 ...

  2. OC内存管理、非ARC机制、MRR机制

    在Xcode里面,默认为ARC(auto reference counting),也就是自动内存管理机制,在这里我们要了解的是内存管理,肯定是不能让系统帮我们管理内存,我们需要将ARC关闭,首先在左边 ...

  3. iOS内存管理编程指南

    iOS 内存管理 目录[-] 一:基本原则 二:成员变量的内存管理 三:容器对象与内存管理 四:稀缺资源的管理 五:AutoRelease 六:其他注意事项 iOS下内存管理的基本思想就是引用计数,通 ...

  4. iOS另类的内存管理

    iOS的内存管理算是老生常谈的问题了,我们写iOS的时候无时无刻不在涉及到内存管理.从开始的MRR(manual retain-release)到后来ARC(Automatic Reference C ...

  5. iOS内存管理策略和实践

    转:http://www.cocoachina.com/applenews/devnews/2013/1126/7418.html 内存管理策略(memory Management Policy) N ...

  6. [Objective-C语言教程]内存管理(36)

    内存管理是任何编程语言中最重要的过程之一.它是在需要时分配对象的内存并在不再需要时取消分配的过程. 管理对象内存是一个性能问题; 如果应用程序不释放不需要的对象,则应用程序会因内存占用增加并且性能受损 ...

  7. iOS 内存管理(转载)

     N久没维护这个博客了,从开始接触编程到现在已经三四年了.不太习惯写博客,这应该是个不好的习惯.所以从哪哪天开始,我得改变自己 (:       . 文采不太好,因此很多的文章都会借鉴他人的,但是我一 ...

  8. ios内存管理笔记(一)

  9. 说说iOS与内存管理(上)

    http://www.cocoachina.com/ios/20150625/12234.html 说起内存管理,看似老生常谈,而真正掌握内存管理的核心其实并不简单.ARC/MRR以及“谁分配谁就负责 ...

随机推荐

  1. 审核流(2)流程设计-SNF.WorkFlow功能使用说明--SNF快速开发平台3.1

    流程设计 图形化的流程设计,更方便.直观 1.打开“流程设计“程序,如上.点击”新建“如下: 2.红色部分为必填项,审批对象是选择要审批的程序菜单,单据名称是在审核流流转时用于提示的单据名称,还要选择 ...

  2. Ubuntu主题美化--使用WPS风格

    五一就这么过去了,我也没有出去玩,一个人闲的蛋疼,无聊就把ubuntu美化一下. 闲话不多说,先看效果: 壁纸是我自己制作的的,如果喜欢另存一下下面这张图设置成背景就可以了,分辨率是1366x768. ...

  3. IOS雕虫小技

    1,你所不知道的Mac截图的强大 2,抓包工具WireShark开发必备,需要装X11插件 3,Mac远程控制Windows桌面-CoRD.或者TeamViewer 4,Mac下解压缩BetterZi ...

  4. ADT-bundle(Android Development Tools)环境配置

    Android开发环境有两套比较主流的:ADT-bundle和Android Studio,前者是Eclipse插件的形式进行开发,后者是Android的官方IDE. ADT环境的配置与调试:(1)安 ...

  5. ruby -- 基础学习(五)empty、nil、blank三者之间的区别

    这三个方法在ROR中经常用到,都是用来判断是否为空的. 区别是: ruby的方法:.nil?..empty? rails的方法 :.blank? 用法的区别: .nil?    :   判断对象是否存 ...

  6. 设计前沿:25款精妙的 iOS 应用程序图标

    在这篇文章中,我为大家精心挑选的25款巧妙设计的 iOS 应用程序图标,会激发你未来的工作.苹果的产品总是让人爱不释手,设计精美,对用户使用体验把握得淋漓尽致,iPhone.iPad.iPod和 iM ...

  7. SQL 分类统计函数

    SELECT TransactionNumber,SUM(CASE WHEN ReasonLevel=0 THEN           TransactionNumber ELSE 0 end ) a ...

  8. 自定义UICollectionViewController之后 如何设置UICollectionView的布局方式

    我们很多时候使用UICollectionView 可能都是直接创建 UICollectionView   通过初始化的时候  传入一个布局对象的方式来使用UICollectionView 比如我们之前 ...

  9. nodejs+express+jade+mongodb给我baby做个小相册(2)-留言板

    上一篇简单的实现了下照片的展现跟浏览功能,这一篇我将给这个程序添加一个留言的功能.那么留言的话肯定要涉及到数据持久了,其实对于这个小功能的话,用个xml就可以,不过为了看起来更加高大上,我决定使用mo ...

  10. 重构第14天 分离职责(Break Responsibilities)

    理解:面向对象的五大特征: SOLID,其中S就是职责单一原则.分离职责指当一个类有许多职责时,将部分职责分离到独立的类中,这样也符合面向对象的五大特征之一的单一职责原则,同时也可以使代码的结构更加清 ...