Objective-C的内存管理机制-MRC

from:http://www.newme.me/diary/51131510

Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制是不同的,它本质上还是C语言中的手动管理方式,只不过稍微加了一些自动方法。

Objective-C的对象生成于堆之上,生成之后,需要一个指针来指向它。例如:Obj *obj = [[Obje alloc]init]
Objective-C的对象在使用完成之后不会自动销毁,需要执行dealloc来释放空间(销毁),否则内存泄露。 [obj dealloc]; 但是 Objective-C大多不直接调用dealloc,而是调用release的,下面就是解释其原因:
Objective-C采用了引用计数(ref count或者retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。
要想获得该对象的时候就用retain,[obj retain]
Objective-C指针赋值时,retain count不会自动增加,需要手动retain。
ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 2 – 1 = 1
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 0,对象被销毁
问题解决!注意,如果没有调用[obj2 release],这个对象的retain count始终为1,不会被销毁,内存泄露。

上面已经比较详细的讲解了Objective c的内存管理。但是还有一个经典的问题没有提到,就是autorelease pool。为什么说这个东西经典那?下面我们就来仔细的剖析。不管在c还是在c++里面,我们每次在内存中开辟了一块空间都要,一定要手动的来释放。然而往往一个大的工程里面有很多对象,对象的释放就成了一件很头疼的事,很可能开辟了一个对象但是忘记释放了就会出现内存泄露。 有时候为了解决这些的问题,我们常常会总结出一些小诀窍,例如:malloc和free,new和delete要成对出现,避免内存泄露。 但是有时候代码会欺骗我们,例如下面一个简单的工厂模式代码:

class CPU
{
};

class SimpleFactory
{

public:

static CPU * GetCPU()
{
return new CPU;
}
};

int main()

{

CPU * cpu = SimpleFactory::GetCPU();

delete cpu;

return 0;

}

假设这个简单工厂模式是系统封装的API,我们只是调用API的GetCPU()方法,然后就得到了一个对象CPU。那么有的程序员就会困惑了,没有出现new你为何要delete哪?所以往往因为这些原因造成了内存泄露,C++的一个解决方法就是用智能指针,把对象的开辟和释放放到堆栈上,这样一个函数调用完,必定释放堆栈中的变量,这样内存中的空间就被释放了。如果对c++了解不清楚的话可能会迷惑为什么在堆栈上分配自由空间就不用考虑内存释放了,括号中的内容作为解释,有兴趣的话可以看一下。
(在空闲存储区使用指向对象的指针有一个问题,就是不会自动析构该对象,换言之,当指针变量的生命周期结束时,在指针本身被销毁的同时它所指向的不会被自动销毁掉.因此,需要人为地调用delete,以调用对象的析构析构函数,从而释放占用的空间.如果不调用delete,就一定会出现内存泄漏.但是,堆栈对象不存在这个问题,因为它可以通过自动弹出对象来调用析构函数,所以有时候在一个函数中你可以看到这样的定义 MyClass obj;这个obj对象就是在堆栈中分配的空间,定义出来直接可以用,初始化方法和析构方法都会自动调用,不必担心内存问题。但是如果你这样写MyClass obj = new MyClass(),那就是在自由的空间中分配内存了,必须delete。大家知道一般程序的堆栈大小不过2M左右,所以如果用到大块内存就必须在自由空间中分配了,但是用了new就要手动的delete,有时候我们就会常常忘记造成内存泄露.
一个解决方法就是将指针隐藏在类类型对象中,为该类编写析构函数隐式地删除该对象指针所指向的空闲存储区中的对象.然后当该对象超出作用域后,析构函数被调用,这样就释放了指针所指向的对象占用的空间,显然就不会内存泄漏了.
令人高兴的是C++标准库中已存在一个非常好的与这些对象对应的类.即std::auto_ptr,它的定义在头文件memory中.例如我这样定义1 auto_ptr obj,这样我们随便用obj不用考虑内存开辟和释放的问题,关键的是MyClass所占的内存还是在自由的空间中分配的,也不会占用堆栈的空间。其实在这里我们可以把auto_ptr这个整体看成一个对象,而MyClass只是它里面的一个成员,相当于MyClass的容器类,智能指针管理内存的方法无非是在这个容器类的初始化方法时在自由空间给MyClass开辟内存,而在容器类的析构中释放自由空间中的内存。)
而Apple的工程师可不认为这是个好方法,他们采用了autorelease。autorelease会使引用计数减1。不过它和release的不同之处是,引用计数为0的时候,对象并不会立刻释放,而是等待autoreleasepool的释放。 AutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。AutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么autorelease之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么 autorelease之后,retain count大于0,此对象依然没有被销毁。所以上面的工厂模式在objective c中实现就会像下面的代码

@interface CPU : NSObject
{
}

@interface SimpleFactory : NSObject
{
}

+ (CPU *)getCPU;

@end
// .mm文件,类的实现。

@implementation SimpleFactory

+ (CPU *)getCPU

{

return [[[CPU alloc]init] autorelease];

}

@end

int main()

{

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

CPU * cpu = SimpleFactory::GetCPU();

[pool release];

return 0;

}

这样objective c中所有的alloc retain和release autorelease都是成对出现的。代码中不会出现release而没有alloc retain的情况。这样就可以大大减少程序员不认真而造成的内存泄露问题,因为只要你自己的代码中有alloc retain关键字的时候你释放就行了,其他情况都不用考虑,你仅仅只要在函数中加上NSAutoreleasePool就行了。与C++智能指针相比,苹果的方法可能更耗时间,不能充分利用编译器的特性,也许性能方面不够高明,但是对于程序的可读性,可理解性来说,苹果的工程师考虑的更周到,更胜一筹。因为编程虽说是让计算机执行的,但是本质上让人看懂,才能使你的代码更好维护和进一步开发。所以那些突发奇想的天才想法,还是留给科学家们给人们创造出更加方便和快捷的工具吧。作为工程师还是合作最重要的,真正现实中我们需要的东西还是需要稳定和可靠的。我们开发的产品可不能作为不确定的试验品。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值