ios内存管理的个人总结
看了蛮多的说是有关ios内存的文章,说什么的都有;而且也没多少说的很全面的,自己动动手记录下自己感觉比较正确且有用的内容好了,仅是到目前为止我自己所了解范围内的,后面有知道新的内容就慢慢更正好了。
一、以下到分割线之前便是ios内存管理的相关内容,一些原理和介绍内容(这些网上一大堆的);分隔线之后是一些代码实例
ios的对象继承于NSObject, 该对象有一个方法:retainCount (内存引用计数) ;当成员变量什么的在声明时期retaincount值为0,我们要使内存没什么泄露也就是要让其retaincount值为0。
*如果最后引用计数大于0 则会内存泄露 leak
*如果引用计数等于0还对该对象进行操作,则会出现内存访问失败,崩溃,crash 所以尽量设置为nil
*这两个问题都很严重,所以请一定注意内存释放和不用过后设置为nil.
在对象的 dealloc函数中释放对象所拥有的实例变量: [XXX release];
在- (void)viewDidUnload中设置: XXX=nil //这个在ios6.0的时候已经遗弃掉了Deprecated
首先就列举下会使retaincount+1的操作:
alloc, allocWithZone, new(带初始化)为对象分配内存 retaincount为1
copy, mutableCopy 复制一个实例,retaincount为1(被复制对象retaincount不变)
retain retaincount+1
另外,名字中带有alloc, copy, retain 字串的函数也都认为会为引用计数加1。。
使retaincount-1的操作:
release 基本上我只用这个
autorelease 这个我基本不用,很不稳定(不过可以和NSAutoreleasePool一起用似乎效果不错,后面会有介绍;不过我不喜欢)
今天2013-8-8,我重新标记下:现在我很多时候都用了autorelease,有时也用release,不过这两个最好不要太多的用在一起,容易导致混淆的;现在我基本上是函数内部的局部变量有可能用到release:在函数内部初始化、使用赋值,然后立即release。
copy 和 retain 的区别(这个觉得网上某文章写的不错,摘抄下来了)
copy: 建立一个索引计数为1的对象,然后释放旧对象
retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
那上面的是什么该死的意思呢?
Copy其实是建立了一个相同的对象,而retain不是:
比如一个NSString对象,地址为0×1111,内容为@”STR”
Copy到另外一个NSString之后,地址为0×2222,内容相同,新的对象retain为1,旧有对象没有变化
retain到另外一个NSString之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1
对象生命周期(alloc,init,retain,release,autorelease)
1. 对于alloc/new/copy/mutablecopy产生的对象要负责release掉,因为这些方法会加引用计数.
2. 其它方法创建的对象,如类工厂方法(+), (id)以及带error的,产生的引用不需要主动释放(如果没有主动retain的话).
3. 函数返回值.如在函数中定义的变量需要返回给调用者,则使用autorelease方法.
AutoreleasePool需要手工创建,不过在新建一个iphone项目时,xcode会自动帮我们写好的。
以下内容摘自网上:
Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。(感谢网友hhyytt和neogui的提醒,某些情况下,系统会自动创建autorelease pool, 请参见第四章)
int main (int argc, const char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; int i, j; for (i = 0; i < 100; i++ ) { NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; for (j = 0; j < 100000; j++ ) [NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。 [loopPool release]; } [pool release]; return (0); } // main
属性@property与内存管理:
eg:如果很多函数都需要改变这个对象,或这个函数会触发很多次,建议使用属性。
关于assign、retain和copy:
assign是系统默认的属性特性,它几乎适用于OC的所有变量类型。对于非对象类型的变量,assign是唯一可选的 特性。但是如果你在引用计数下给一个对象类型的变量声明为assign,那么你会在编译的时候收到一条来自编译器的警告。因为assign对于在引用计数下的对象特性,只创建了一个弱引用(也就是平时说的浅复制)。这样使用变量会很危险。当你release了前一个对象的时候,被赋值的对象指针就成了无头指针了。因此在为对象类型的变量声明属性的时候,尽量少(或者不要)使用assign。
// assign -(void)setTestObject :(id)newValue{ testObject= newValue; } // retain -(void)setTestObject :(id)newValue{ if (testObject!= newValue) { [testObject release]; testObject= [newValue retain]; } } // copy -(void)setTestObject :(id)newValue{ if (testObject != newValue) { [testObject release]; testObject = [newValue copy]; } }
asssign 相对于指针赋值,不对引用计数进行操作,注意原对象不用了,一定要把这个设置为nil
retain 相当于对原对象的引用计数加1
copy 不对原对象的引用计数改变,生成一个新对象引用计数为1
例如:如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。
因此,delegate往往都设计成assign方式的属性,而不是retain属性的。也就是说并没有获得所有权,这是保证不产生循环问题的条件。
对于属性的初始化最好是使用以下两种方法:
//第一种: UIView *view = [[UIView alloc] init]; //mystudent 对象 retainCount = 1; self.view = view; //student 对象 retainCount = 2; [view release]; //student 对象 retainCount = 1; //第二种: self.view = [[[UIView alloc] init]autorelease];
根据上面两种类型的初始化方法,在每个时候分别输出他的retainCount值如下:(这里仅是输出看下retainCount值,实际操作是不要这么多release)
UIView *view1 = [[UIView alloc]init]; NSLog(@"view & retainView Count: %d %d",[view1 retainCount],[self.retainView retainCount]); self.retainView = view1; NSLog(@"view & retainView Count: %d %d",[view1 retainCount],[self.retainView retainCount]); [view1 release]; NSLog(@"view & retainView Count: %d %d",[view1 retainCount],[self.retainView retainCount]); [view1 release]; NSLog(@"view & retainView Count: %d %d",[view1 retainCount],[self.retainView retainCount]);//这里输出的数值和前一个相同,不知道是内部怎么操作的 [view1 release]; NSLog(@"view & retainView Count: %d %d",[view1 retainCount],[self.retainView retainCount]); //这以后不能操作了,会crash的(都已经释放完了) UIView *view2 = [[UIView alloc]init]; NSLog(@"view & assignView Count: %d %d",[view2 retainCount],[self.assignView retainCount]); self.assignView = view2; NSLog(@"view & assignView Count: %d %d",[view2 retainCount],[self.assignView retainCount]); [view2 release]; NSLog(@"view & assignView Count: %d %d",[view2 retainCount],[self.assignView retainCount]);//这里输出的数值和前一个相同; [view2 release]; NSLog(@"view & assignView Count: %d %d",[view2 retainCount],[self.assignView retainCount]);
输出的结果分别如下:似乎有个release操作后数值没变化,不知道是怎么搞的
============================================================================================= 二、下面一些特殊情况列举:
1、在页面中将某一对象(视图)设置成属性,并将该对象(视图)附加到该页面上,在释放的时候不能遍历该页面的所有子视图去找这个视图然后去释放,如果这么做了就相当于仅释放了那个视图,而属性依旧存在。 额...说的比较抽象,来个实例:
//定义属性 @property (nonatomic, assign) UIView *coverView; //初始化 if (!self.coverView) { self.coverView = [[[UIView alloc]initWithFrame:appDelegate.window.frame]autorelease]; [self.view addSubview:self.coverView]; } //释放,比较重要看这里 //需要使用这种: [self.coverView removeFromSuperview]; self.coverView = nil; //不能使用这个遍历释放,这样self.coverView会仍然在的 for (UIView *subView in self.view.subviews) { if ([subView isKindOfClass:[UIView class]]) { [subView removeFromSuperview]; subView = nil; } }