IOS程序内存管理
http://blog.csdn.net/lamp_zy/article/details/8167742
一、非ARC方式,即手动引用计数方式:
这种方式使用引用计数来控制对象的释放
1.alloc,copy,retain三个函数(包括名字中带有着三个词的函数)每调用一次引用计数加1
alloc:分配一块内存用于存放调用类的对象
retain:保有这个对象,通过使其引用计数加1来实现保有,retain返回的对象的指针如果不调用release,这个对象的引用计数至少还有1,同个这样来控制保有这个对象
copy:新开辟一块内存,把调用对象拷贝到里面,copy返回的对象跟被copy的对象完全独立,改变其中一个不会影响另一个
2.release,autorelease每调用一次,引用计数减1:
autorelease:
ClassA *Func1()
{
ClassA *obj = [[[ClassA alloc] init] autorelease];
return obj;
}
每一个runloop,系统会隐式创建一个autorelease pool,每个runloop结束时,对应autorelease pool会销毁,pool中每个object都会release一次
一次事件就产生一个runloop,如一次鼠标点击,键盘点击,一次触摸,一次异步http连接并接收完数据等
也可以自己创建autorelease pool,比如在一个循环中用到了太多的临时变量,在整个循环结束(或每一次循环结束)后想让其自动release:
void main()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *args = [[NSProcessInfo processInfo] arguments];
unsigned count, limit = [args count];
for (count = 0; count < limit; count++)
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
NSString *fileContents;
NSString *fileName;
fileName = [args objectAtIndex:count];
fileContents = [[[NSString alloc] initWithContentsOfFile:fileName] autorelease];//调用了autorelease,才会进入autorelease pool
// this is equivalent to using stringWithContentsOfFile:
/* Process the file, creating and autoreleasing more objects. */
[loopPool release];
}
/* Do whatever cleanup is needed. */
[pool drain];
exit (EXIT_SUCCESS);
}
drain和release在引用计数环境中(ios)是一样的,release自身的同时遍历pool中所有对象进行release,在垃圾回收环境中(mac,ios不支持垃圾回收),release不做任何事情,drain则能触发垃圾回收,所以,一般用drain,以后的兼容性更好
autorelease的执行效率并不高,不能马上释放需要释放的内存,可能导致积压很多需要释放的内存,所以尽量不用用autorelease
3.引用计数等于0时,会调用对象的dealloc用于析构对象,dealloc并不影响引用计数,永远不要显式调用它,要完全依赖引用计数机制
4.NSString/set等容器在对象加入后,对象的引用计数会加1以得到其控制权,移除对象时,对应引用计数减1,UIView对subView的控制,UINavigationController对其内的controller等其他类似容器的对象对其子对象也是一样的机制
5.循环引用:
a对象中一个方法alloc了一个b对象,并调用b对象的一个方法funb,b对象的funb中,alloc了一个c对象,调用了c对象的func,在c对象的func中,又alloc了一个b对象
这样下来,b对象的引用计数为2,c对象的引用计数为1
a对象处理完后调用b对象的release,本来希望释放掉b对象(a对b alloc一次,按理只要release一次就可以释放b),但是由于循环引用(b和c互相引用),导致b对象release一次后,引用计数还有1,没有释放,b和c都是在dealloc中释放对方的,b没有调用dealloc,c也就不会调用dealloc,这样就形成了b和c的内存泄漏
如何避免循环引用造成的内存泄漏呢:
以delegate模式为例(viewcontroller和view之间就是代理模式,viewcontroller有view的使用权,viewcontroller同时也是view的代理(处理view中的事件)):
UserWebService.h
- #import<UIKit/UIKit.h>
- //定义一个ws完成的delegate
- @protocol WsCompleteDelegate
- @required
- -(void) finished;//需要实现的方法
- @end
- @interface UserWebService:NSObject
- {
- id <WsCompleteDelegate> delegate;//一个id类型的dategate对象
- }
- @property (assign) id <WsCompleteDelegate> delegate;
- -(id)initWithUserData:(User *)user;
- -(void)connectionDidFinishLoading:(NSURLConnection *)connection;
- @end
UserWebService.m:
#import <Foundation/Foundation.h>
@systhesize delegate;//同步这个delegate对象
@implementation UserWebService
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[delegate finished]
}
@end
LoginViewController.h:
#import "UserWebService.h" //包含含有委托的头文件
@interface LoginViewController:UIViewController <WsCompleteDelegate>
-(void)submit;
@end
LoginViewController.m:
@implementation LoginViewController
-(void) submit
{
User *user = [[User alloc]init];
[user setUserId:@"username"];
[user setPassword:@"password"];
ws = [[UserWebService alloc] initWithUserData:user];
ws.delegate = self;//设置委托的收听对象
[user release];
[ws send];
}
//实现委托中的方法,
-(void) finished
{
NSAttry *users = [ws users];
}
@end
可以看到,delegate声明为assign:
@property (assign) id <WsCompleteDelegate> delegate;
如果声明为retain会如何?
LoginViewController alloc了一个UserWebService,UserWebService的代理又是LoginViewController,这样就循环引用了:
ws = [[UserWebService alloc] initWithUserData:user]; //UserWebService对象引用计数加1
ws.delegate = self;//LoginViewController对象引用计数加1
外部框架allocLoginViewController对象后,LoginViewController对象的引用计数为2 ,release后还是无法销毁,产生内存泄漏
所以用assign而不是retain声明属性可以避免循环引用,ARC下用弱引用也可以解决
6.内存检测可以用xcode继承的instrument工具,不过循环引用引起的内存泄露是检测不出来的
7.对象release到引用计数为0后,如果对应指针没有赋值为nil,怎出现野指针
8.ClassA *a = [[ClassA alloc] init];
a = nil;//alloc的内存没有任何对象可以控制它了,引用计数永远为1,这就造成了内存泄露
9.简单的赋值操作并不会改变对象的引用计数:
ClassA *a = [[ClassA alloc] init];
ClassA *b = a;//a和b指向的对象的引用计数还是1
10.@property:
默认为@property为@property(atomic,assign)
nonatomic: 没有对应的atomic关键字,即使上面是这么写,但atomic叧是在你没有声明这个特性的时候系统默认,你无法主动去声明这一特性。nonatomic不支持多线程访问,atomic有同步机制,支持多线程访问,如果需要多线程访问,声明为atomic(维持默认),否则声明为nonatomic,因为nonatomic效率比atomic高得多
关于assign、retain和copy: assign是系统默认的属性特性,它几乎适用亍OC的所有变量类型。对于非对象类型的变量,assign是唯一可选的特性。但是如果你在引用计数下给一个对象类型的变量声明为assign,那么你会在编译的时候收到一条来自编译器的警告。因为assign对于在引用计数下的对象特性,叧创建了一个弱引用(也就是平时说的浅复制)。返样使用变量会很危险。当你release了前一个对象的时候,被赋值的对象指针就成了无头指针了。因此在为对象类型的变量声明属性的时候,尽量少(或者不要)使用assign。
关于assign合成的setter,看起来是这样的:
-(void)setObjA:(ClassA *)a {
objA = a;
}
在深入retain之前,先把声明为retain特性的setter写出来:
-(void)setObjA:(ClassA *)a
{
If(objA != a)
{
[objA release];
objA = a;
[objA retain]; //对象的retain count 加1
}
}
明显的,在retain的setter中,变量retain了一次,那么,即使你在程序中 self.objA = a; 只写了这么一句,objA仍然需要release,才能保证对象的retain count 是正确的。但是如果你的代码 objA = a; 叧写了这么一句,那么这里只是进行了一次浅复制,对象的retain count 并没有增加,因此这样写的话,你不需要在后面release objA。 这2句话的区别是,第一句使用了编译器生成的setter来设置objA的值,而第二句叧是一个简单的指针赋值
11.NSString *str = [[NSString alloc] initwithstring @“abc”];
str = @“abcd”;‘
[str release];
NSLog("%@",str);//打印出abcd
str为什么没有变成野指针呢?因为字符串常量(包括NSString和@“......”)的引用计数很大(100K+),基本上不会释放掉(由OC自己管理),所以字符串常量可以不用release
二、ARC(auto release count):自动引用计数方式:
这种方式使用强引用和弱引用来控制对象的释放,只要对象的强引用计数为0,对象就立即释放,变量默认为强引用
强/弱引用的数量是指向这个对象的强/弱指针的数量
弱引用:
在ARC方式下:
NSString __weak *string = [[NSString alloc] initWithFormat:@"First Name: %@", [self firstName]]; |
NSLog(@"string: %@", string); |
打印出来的是null,因为这个NSString对象创建完成后,没有任何强引用指向它,所以立即释放了,并赋值为nil
__unsafe_unretained:弱引用变量声明,等同于assign
__weak:声明了一个可以自动nil化的弱引用,释放后,变量自动置为nil
__strong:强引用,等同于retain
ARC在IOS4.0及以后的系统都可以用,但是weak声明必须在ios5及以后才可以用,如果启用了RAC,选择目标运行平台为ios4,xcode会自动插入一个兼容库到工程中,以实现自动兼容
ARC引入的新规则:
ARC能够起作用引入了一些新的规则。这些规则定义了所有的内存管理方方面面,某些规则是为了更好的体验,还有一些规则是为了减少编程人员在内存管理方面的工作。如果你违反这些规则,就会得到编译期的错误,而不是运行期的错误。
-
你不能显示调用dealloc,不能实现和显示调用retain,release,retainCount和autorelease。
当然也不能使用
@selector(retain),@selector(release)等相关的功能。 -
你不能在C结构中使用实例指针。
与其使用结构,建议你使用Objective-C类来管理数据。
-
id和void *不能进行隐式转换。
你必须显式的告诉编译器这个转换的类型。
-
不能使用
NSAutoreleasePool的实例.作为替代,ARC提供
@autoreleasepool块作为替代。后者提供了更灵活的方式。
为了和手动管理内存相兼容,ARC定义了函数和变量命名一条规则:
-
属性变量的命名不能使用new开始。

浙公网安备 33010602011771号