1. 要避免两个对象互相retain的问题;
2. Core Foundation's memory allocation policy is that you need to release values returned by functions with “Copy” or “Create” or "alloc" in their name;
这是指在corefoundation.framework中。而NSString等是在foundation.framework中。
3.不是alloc出来的对象不要执行 autorelease!!!
alloc方法将分配的内存初始化为0,BOOL类型变量被初始化为NO,int为0,float为0.0,指针为nil,所有的基地都属于我们了。
4.Cocoa过去没有垃圾回收机制,iPhone现在也没有。所以必须自己通过-retain, -release and -autorelease这些命令使用引用计数(reference counting)技术来管理内存。
Objective-C的内存管理机制与.Net/Java那种全自动的垃圾回收机制不同,它本质上还是C语言中的手动管理方式,只不过加了一些自动方法。
为什么iOS中没有GC
• GC消耗CPU时间,耗电。
• GC执行后,会停掉运行时库;
◦ GC执行频率高损失性能:耗内存,影响UI动画处理效果;
5.可以不为应用程序委托AppDelegate提供dealloc方法,因为它永远不会被调用。iPhone OS在应用程序拆卸(tear-down)期间会恢复所有应用程序内存。从技术上讲,视图控制器存在泄漏问题。但是在实践中,这不是问题。
6. 在objc 中出现了EXE_BAD_ACCESS,经常是因为访问了被释放了的对象。
在dealloc时,要把[super dealloc]放到 dealloc的最后调用,否则可能会出现问题,可能是因为释放了NSObject,导致继承该类的其它数据成员没有宿主,导致错误。
7.内存使用经验:
(1)使用尽量少使用或使用小尺寸的UIView层
对于iPad,一个像素需要4Byte来算,一个层的大小就是 4x1024x768 ~ 3M字节, 10个层就是 10x3M = 30M
(2)尽量使用图片pattern,而不是一张大的图片
与在界面上放一个大底图相比,最佳方案是,设计出一个小的pattern图,然后用这个方案显示成底图。
UIImage *smallImage = [[UIImage alloc] initWithContentsOfFile:path];
backgroundView.backgroundColor = [UIColor colorWithPatternImage:smallImage];
[smallImage release];
(3)使用 NSAutoreleasePool 的时候也尽量建议局部使用,比如下面的循环。
循环中大量生成的自动释放autorelease对象,可以考虑使用autorelease pool封装。代码范例:
for(UIView *subview in bigView.subviews) {
// 使用autorelease pool自动释放对象池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImageView *imageView = (UIImageView *)subview;
// subview处理代码
.......
// 销毁自动释放对象
[pool drain];
}
(4)iOS4.0的multi-tasking特性发布后,程序可以被调入后台运行,苹果工程师的意见是,进入后台运行时,你的应用应该释放掉能释放的对象,尽量保持在16M左右,这样别的程序运行时才不容易把你的应用挤掉。
(5)生成对象时,使用autorelease; 对象代入时,先autorelease后再retain,Failed Self 的原则;对象在函数中返回时,使用return [[object retain] autorelease];
8.数组和字典的内存管理:
当NSMutableArray被release时,它将自动release所有索引位置上的对象. 应该同样适用于NSArray及其它容器.
测试NSMutableArray addObject会对添加进的对象发送retain消息;NSMutableDictionary setObject也会对添加的对象发送retain消息;
当使用AppKit时,Cocoa定期自动创建和销毁自动释放池.通常是在一个事件循环后执行这些操作.
<<objective-c基础教程>>中讲,实际上,在编写iPhone程序时,苹果公司建议你不要在自己的代码中使用autorelease方法,同时还要避免使用那些创建了自动释放对象的便利函数.
9.ios6.0内存警告的兼容处理 viewDidUnload 屏蔽
http://www.cocoachina.com/bbs/simple/?t125949.html
判断self.view是否显示在屏幕上
if (self.view.window)
{} //在iOS程序里面,window是程序视图层次体系的最高层。view要直接或间接加到这个window上才能被看到。即它的window属性值不为nil。
模拟内存警告:
在ios6模拟器中,当前页didReceiveMemoryWarning被调用,前一页didReceiveMemoryWarning被调用,此时前一页self.view.window为nil,这里如果置前一页的self.view=nil,再返回前一页时,前一页loadview被调用。
在ios5模拟器中,当前didReceiveMemoryWarning被调用,前一页的didReceiveMemoryWarning、viewDidUnload被调用。再返回前一页,会调用前一页的loadview。
结论:在ios5中,会自动销毁非当前页的viewController.view。ios6中,要手动销毁非当前页的viewController.view,且不会调用viewDidUnload。只有viewController.view被销毁了,viewController的loadview,viewDidLoad才会被依次调用。在调用loadView前,self.view会被重新创建,它的内容就是空的了。
所以,基于原ios5的viewDidUnload代码,在ios6中处理代码修改如下:
-(void)didReceiveMemoryWarning{
[super didReceiveMemoryWarning];
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0)
{
if ([self.view window] == nil)//非当前视图
{
[self viewDidUnload];
/*
下面self.view = nil;目的是再次进入时能够重新加载,
此操作不能调用两次,如果调用两次会导致此页面在下面时也会被调用loadview;
且要销毁所有的子view,在loadview时重新创建,这涉及到保存一些view中的数据,防止丢失。
*/
}
}
}
如果不主动执行[self viewDidUnload]或相关函数,则必须要在loadView和viewDidLoad中对一些成员的创建做空建判断。空建即为空是才创建。但[self.view addSubview:xxx];要在空建判断外添加,因为此时self.view内容是空的。
参考 http://www.cnblogs.com/thefeelingofsimple/archive/2012/12/03/2799145.html
10.
9.ARC
http://onevcat.com/2012/06/arc-hand-by-hand/
http://www.yifeiyang.net/development-of-the-iphone-simply-1/
在Building Setting中,Apple LLVM compiler 3.1 language 中 “Objectice-C Auto Reference Counteting” 项设置是否启用ARC。
如果只想对某个.m文件不适应ARC,可以只针对该文件加上 -fno-objc-arc 编译FLAGS。而启用arc使用 -fobjc-arc编译选项。
ARC的一个基本规则即是,只要某个对象被任一strong
指针指向,那么它将不会被销毁。如果对象没有被任何strong指针指向,那么就将被销毁。成员对象指针和局部指针默认就是strong的,即ARC中默认的指针类型就是strong
。
当对象被销毁后,在ARC机制作用下,所有指向这个对象的weak
指针将被置为nil
。这个特性相当有用,使用ARC以后,不论是strong
还是weak
类型的指针,都不再会指向一个dealloced的对象,不会出现EXCBADACCESS错误,从根源上解决了意外释放导致的crash。
strong和retain相似,weak比assign更聪明一些。
ARC基本规则
- retain, release, autorelease, dealloc由编译器自动插入,不能在代码中调用
- 可以重写dealloc,但不能调用[super dealloc];
引用关键字
ARC中关于对象的引用参照,主要有下面几关键字。使用strong, weak, autoreleasing限定的变量会被隐式初始化为nil。
__strong 强参照,变量声明缺省都带有__strong关键字。__weak 弱参照,该类型不影响对象的生命周期,如果对象之前就没有持有者,那么会出现刚创建就被破弃的情况。弱参照还有一个特征,即当参数对象失去所有者之后,变量会被自动付上nil (Zeroing)。
__unsafe_unretained 即MRC的assign
__autoreleasing 该关键字使对像延迟释放。用在函数返回新对象或函数的参数会接管新对象。可以理解为暂只保存对象内存不检查强弱情况,待有指针接管后再检查判断。
- (void) generateErrorInVariable:(__autoreleasing NSError **)paramError { .... *paramError = [[NSError alloc] initWithDomain:@"MyApp" code:1 userInfo:errorDictionary]; }
NSError *error = nil; [self generateErrorInVariable:&error]; NSLog(@"Error = %@", error);
-(NSString *)stringTest { __autoreleasing NSString *retStr = [NSString alloc] initWithString:@"test"]; return retStr; }
即当方法的参数是id*,或希望方法返回时对象被autoreleased,那么使用该关键字。
伴随ARC的导入,还有一系列函数的定义也被严格定义了,那就是以 init 开头的函数。init 函数作为alloc生成对象的初期化函数,需要按原样直接传递对象给调用段,所以下面的声明是OK的。
-(id)initWithObject:(id)obj;
而下面的是NG的。
-(void)initWithObject;
不过声明为 -(void) initialize; 是没有问题的。
只能将ARC用在objective-c对象上(也即继承自NSObject的对象),但是如果涉及到较为底层的东西,比如Core Foundation中的malloc()或者free()等,ARC就鞭长莫及了,这时候还是需要自己手动进行内存管理。另外为了确保ARC能正确的工作,有些语法规则也会因为ARC而变得稍微严格一些。
ARC确实可以在适当的地方为代码添加retain
或者release
,但是这并不意味着你可以完全忘记内存管理,因为你必须在合适的地方把strong
指针手动设置到nil,否则app很可能会oom。简单说还是那句话,你必须时刻清醒谁持有了哪些对象,而这些持有者在什么时候应该变为指向nil
。
使用ARC之后,由于内存问题造成的crash基本就是过去式了,OOM除外。
iOS 5.1 Xcode 4.3版本中,ARC 有效时的属性(@property) 定义的时候,如果不明确指定所有权关键字,那么缺省的就是 strong。但是在 Xcode4.2 中,即使 strong 也要显示指定。
property也可以用strong
或weak
来标记,简单地把原来写retain
和assign
的地方替换成strong
或者weak
就可以了。
代码示例:
__weak NSString *weakName = self.textField.text;
首先,ARC是LLVM3.0编译器的特性,而老的工程默认编译器可能是GCC或者LLVM-GCC,因此第一步就是确认编译器是否正确。在Project设置面板,选择target,在Build Settings中将Compiler for C/C++/Objective-C选为Apple LLVM compiler 3.0或以上。为了确保之后转换的顺利,最好把Treat Warnings as Errors和 Run Static Analyzer都打开,确保在改变编译器后代码依旧没有警告或者内存问题。
Build Settings页面,把Objective-C Automatic Reference Counting改成YES(如果找不到的话请看一看搜索栏前面的小标签是不是调成All了..这个选项在Basic里是不出现的),这样工程就将在所有源代码中启用ARC了。
Edit->Refactor下的Convert to Objective-C ARC,点击后会让选择要转换哪几个文件。
readonly的@property声明要显示声明strong
@property (nonatomic, strong, readonly) NSString *name;
对于强引用的成员view,在出现内存警告时,要在unload中置为nil,