IOS内存管理
很多童鞋对于IOS中的内存管理处理的不好,有时候感觉自己已经掌握了,其实用起来的时候往往出现很多的内存泄露或者因为内存管理的不恰当导致程序崩溃!
网上有一片很详细的内存管理文章,很多人估计嫌长,不愿意去看,我就写一个简单易懂的分享。
我分四块来讲解,这篇文章比较简单,因为我不想把你们搞晕,其实总的来说就一个原则!
一、IOS内存管理的机制
IOS中没有垃圾回收机制(IOS5中好像已经有了,但是目前还不太实用),所以基本一切都是自己手动管理。
IOS中采用引用计数的内存管理方式,啥意思呢?讲的通俗点就是说一块内存地址是否应该被释放是又retaincount来决定的,如果这块内存地址的retaincount为0,那么这块内存就会被释放,再也找不到了。
所以想让他一直存在,那么它的retaincount就必须大于0。
那么如何管理呢?其实原理非常简单,就是谁用谁retain,谁retain的谁release,遵循这个原则的目的就是让你不会出错。
举个例子
网上有一片很详细的内存管理文章,很多人估计嫌长,不愿意去看,我就写一个简单易懂的分享。
我分四块来讲解,这篇文章比较简单,因为我不想把你们搞晕,其实总的来说就一个原则!
一、IOS内存管理的机制
IOS中没有垃圾回收机制(IOS5中好像已经有了,但是目前还不太实用),所以基本一切都是自己手动管理。
IOS中采用引用计数的内存管理方式,啥意思呢?讲的通俗点就是说一块内存地址是否应该被释放是又retaincount来决定的,如果这块内存地址的retaincount为0,那么这块内存就会被释放,再也找不到了。
所以想让他一直存在,那么它的retaincount就必须大于0。
那么如何管理呢?其实原理非常简单,就是谁用谁retain,谁retain的谁release,遵循这个原则的目的就是让你不会出错。
举个例子
ClassA *obj1 = [[ClassA alloc] init];
这里你申请了一块内存地址,指针obj1指向了这块内存地址,同时你给它retain了一下(retain,copy,alloc这几个方法都相当于把retaincount加一),此时obj1所指向的这块内存地址retaincount为1。
接着来(这里我只是举例子让大家了解一下,这些代码没有什么实际意义)
ClassA *obj2=obj1;
[obj2 retain];
接着来(这里我只是举例子让大家了解一下,这些代码没有什么实际意义)
ClassA *obj2=obj1;
[obj2 retain];
好了,大家看一下,obj1和obj2同时都指向了上面所述的内存地址,我假设这块内存为A,那么A这块内存的retaincount为2。
假如大家在之后一直在使用obj1和obj2,最后不用的时候,需要释放了,我希望大家这样来释放
假如大家在之后一直在使用obj1和obj2,最后不用的时候,需要释放了,我希望大家这样来释放
[obj1 release];
[obj2 release];
[obj2 release];
而不希望大家这样来释放
[obj1 release];
[obj1 release];
//或者
[obj2 release];
[obj2 release];
[obj1 release];
[obj1 release];
//或者
[obj2 release];
[obj2 release];
为什么这样呢?上面两种释放方法没有错,都成功将A这块内存的retaincount减为0,导致A被释放了。但是你没有遵循以上所说“谁用谁retain,谁retain的谁release”。
这只是一个小例子,你可能感觉不到问题的严重性,如果你做一个项目,在其中有大量这种retain,release的话,你不遵循这种原则,你会死的很惨,没有条理最后你会被搞的很乱,找都找不到。
二、不要随便看到retaincount多就给它释放
很多朋友在学完内存管理后喜欢玩消除,不该释放的他去释放,举个例子
这只是一个小例子,你可能感觉不到问题的严重性,如果你做一个项目,在其中有大量这种retain,release的话,你不遵循这种原则,你会死的很惨,没有条理最后你会被搞的很乱,找都找不到。
二、不要随便看到retaincount多就给它释放
很多朋友在学完内存管理后喜欢玩消除,不该释放的他去释放,举个例子
UIViewController *ct1=[[UIViewController alloc] init];[self.navigationcontroller pushViewController:ct1 animated:YES];[ct1 relese];
这里,初始化一个viewcontroller,然后用导航push进来,最后释放这个viewcontroller,很多人在这里操作完毕后,继续打印ct1的retaincount,发现它的retaincount不为0,就继续release,这里是非常错误的方法,直接导致程序崩溃,继续看以上原则“谁用谁retain,谁retain的谁release”,你用的时候,初始化,并retain了一次,最后你release了一次,到此结束!之后不管你发现它的retaincount为几,你都不该再release。你打印出来的retaincount并不是你retain的,而是系统框架本身retain的,由框架本身来处理,开发者不必理会。
类似还有很多,数组的addobject、addsubview等等很多方法,都是框架自己家retain的,开发者本身retain后release,不必理会它的retaincount。
另外插一句,如果是多人协同开发,这种情况最容易碰到,别人传递一个参数过来,你拿过来retain后使用,使用完毕release,发现retaincount还不是0,于是你继续release,这样你就把你同伴retain的计数给release掉了,当他下次使用的时候发现崩溃了,这种情况会让你的团队浪费无数的时间来找原因。
类似还有很多,数组的addobject、addsubview等等很多方法,都是框架自己家retain的,开发者本身retain后release,不必理会它的retaincount。
另外插一句,如果是多人协同开发,这种情况最容易碰到,别人传递一个参数过来,你拿过来retain后使用,使用完毕release,发现retaincount还不是0,于是你继续release,这样你就把你同伴retain的计数给release掉了,当他下次使用的时候发现崩溃了,这种情况会让你的团队浪费无数的时间来找原因。
三、关于属性
属性在.h文件中是这样声明的
属性在.h文件中是这样声明的
@property(nonatomic,retain) Class *obj;
括号内第二个参数代表的意义是什么呢?
我们分析一下他内部的机制
@property (retain) Class* obj;和@synthesize obj;实际上是getter和setter,有retain参数的property,内部代码如下
我们分析一下他内部的机制
@property (retain) Class* obj;和@synthesize obj;实际上是getter和setter,有retain参数的property,内部代码如下
-(Class *) getObj
{
return obj;
}
-(void) setObj:(Class *) value
{
if (obj != value)
{
[obj release];
obj = [value retain];
}
}
{
return obj;
}
-(void) setObj:(Class *) value
{
if (obj != value)
{
[obj release];
obj = [value retain];
}
}
愿意仔细分析的童鞋可以仔细研究一下,不太明朗的童鞋请听我说。
这个属性定义以后如何使用呢?我们只说赋值(也就是set)
self.obj=xxx;
[self setObj:xxx];
obj=xxx;
赋值方法有以下几种,其中前两个是相同的,都是调用了setObj:方法同时也就导致了obj的retaincount增加了1,而第三个赋值方法并没有增加retaincount,只是将指针指向了xxx内存地址。
所以大家赋值时可以这样
这个属性定义以后如何使用呢?我们只说赋值(也就是set)
self.obj=xxx;
[self setObj:xxx];
obj=xxx;
赋值方法有以下几种,其中前两个是相同的,都是调用了setObj:方法同时也就导致了obj的retaincount增加了1,而第三个赋值方法并没有增加retaincount,只是将指针指向了xxx内存地址。
所以大家赋值时可以这样
Class *c=[[Class alloc] init];
self.obj=c;[c release];
或者Class *c=[[Class alloc] init];
或者Class *c=[[Class alloc] init];
[self setObj:c];[c release];
或者Class *c=[[Class alloc] init];obj=c;
属性的这个机制是让你在整个self里(也就是当前类的实例里)一直保留这个obj的retaincount,所以你必须在此类的dealloc里将obj的retaincount置零(你只需要release,如果你严格遵守“谁用谁retain,谁retain的谁release”的话)
-(void)dealloc
{
[obj release];
[super dealloc];
}
-(void)dealloc
{
[obj release];
[super dealloc];
}
这里,其实我主要要说明的就是属性的赋值方式用self.xxx=xxxx和xxx=xxxx是完全不同的赋值方式,大家切记!
四、autorelease对象
autorelease对象就是自动释放对象,大家可能会疑惑,能自动释放,我还管理干嘛,我都用自动释放算了,事实不是这样的,自动释放确实好用,但是自己管理内存才能让项目占用更小的资源,跑起来更流畅,大家可以手动管理加autorelease一起来使用,我先讲解一下autorelease对象到底是什么个情况。
首先,告诉大家autorelease对象什么时候才会被释放。
在main函数里有一个autorelease pool,自动释放池,所以在这个自动释放池里的autorelease对象都会在自动释放池结束的时候全部被释放。
大家可能会问了,“难道所有的autorelease对象都是在程序退出的时候才被释放?”,答案当然不是,其实main函数中只是一个程序中众多自动释放池中的一个,每个runloop都会隐性的创建一个自动释放池,啥是一个runloop?每个UIView创建、delegate回调等等都会创建一个自动释放池,记得这个“等等”,意思就是有很多(如果大家想要详细了解runloop,可以去google)。所以大家不用担心autorelease对象不会被释放。
很多系统的方法,比如[NSArray array]、[NSString stringformat]等等返回的对象都是autorelease对象,这些对象是不需要手动释放的,如果手动释放会导致程序崩溃!切记!,原则就是系统中所有没有使用alloc、copy、retain来创建的对象都是autorelease对象,大家千万别手动release!
好了,就说这么多吧,如果有什么错误,希望大家提出,我来改。。。工作太忙,也没啥时间写的更详细!!!望大家见谅!
四、autorelease对象
autorelease对象就是自动释放对象,大家可能会疑惑,能自动释放,我还管理干嘛,我都用自动释放算了,事实不是这样的,自动释放确实好用,但是自己管理内存才能让项目占用更小的资源,跑起来更流畅,大家可以手动管理加autorelease一起来使用,我先讲解一下autorelease对象到底是什么个情况。
首先,告诉大家autorelease对象什么时候才会被释放。
在main函数里有一个autorelease pool,自动释放池,所以在这个自动释放池里的autorelease对象都会在自动释放池结束的时候全部被释放。
大家可能会问了,“难道所有的autorelease对象都是在程序退出的时候才被释放?”,答案当然不是,其实main函数中只是一个程序中众多自动释放池中的一个,每个runloop都会隐性的创建一个自动释放池,啥是一个runloop?每个UIView创建、delegate回调等等都会创建一个自动释放池,记得这个“等等”,意思就是有很多(如果大家想要详细了解runloop,可以去google)。所以大家不用担心autorelease对象不会被释放。
很多系统的方法,比如[NSArray array]、[NSString stringformat]等等返回的对象都是autorelease对象,这些对象是不需要手动释放的,如果手动释放会导致程序崩溃!切记!,原则就是系统中所有没有使用alloc、copy、retain来创建的对象都是autorelease对象,大家千万别手动release!
好了,就说这么多吧,如果有什么错误,希望大家提出,我来改。。。工作太忙,也没啥时间写的更详细!!!望大家见谅!