内存管理以及Autorelease和自动释放池

来源:互联网 发布:mod安装软件 编辑:程序博客网 时间:2024/05/21 11:17

今天闲来无事,对OC的内存管理部分做了一下简单的总结,现在基本都是用的Arc,不过了解一下内存管理也没有坏处

注:ObjC中的内存管理机制跟C语言中指针的内容是同样重要的,要开发一个程序并不难,但是优秀的程序则更测重于内存管理,它们往往占用内存更少,运行更加流畅。虽然在新版Xcode引入了ARC,但是很多时候它并不能完全解决你的问题。在Xcode中关闭ARC:项目属性—Build Settings--搜索“garbage”找到Objective-C Automatic Reference Counting设置为No即可。


一、内存管理

我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象是存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上)。如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存。其他高级语言如C#、Java都是通过垃圾回收来(GC)解决这个问题的,但在OjbC中并没有类似的垃圾回收机制,因此它的内存管理是依靠引用计数来维持的的,对于Mrc的项目,需要程序员手动的管理内存。

1.引用计数器

在ObjC中内存的管理是依赖对象引用计数器来进行的:在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。

2.内存管理原则

手动管理内存有时候并不容易,因为对象的引用有时候是错综复杂的,对象之间可能互相交叉引用,此时需要遵循一个法则:谁创建,谁释放

一般情况下: 
alloc 需要对应一个release或者autorelease

retain、copy  需要对应一个release

这是成对原则

对于属性和成员变量alloc 对应的release可以写在dealloc方法中dealloc方法用来释放这个对象所占的内存(包括成员变量)和其它资源。dealloc方法会在这个对象的retain count为0时会被调用。 

3.例外

IOS 提供了很多static(+) 创建对象的类方法,这些方面是静态的,可以直接用类名调用如:
  NSString *testString = [NSString stringWithFormat:@"test" ];
  testString 是自动管理的对象,你不用relese 他,他有一个很大的retain count, release后数字不变。


二、autorelease与自动释放池

1.自动释放池

自动释放池以栈的形式实现:当你创建一个新的自动释放池时,它将被添加到栈顶。接收autorelease消息的对象将被放入到最顶端的自动释放池中。如 果将一个对象放入一个自动释放池中,然后创建一个新的自动释放池再销毁该新建的自动释放池,则这个自动释放对象仍将存在,因为容纳该对象的自动释放池仍然 存在。

自动释放池(Autorelease pool)是OC的一种内存自动回收机制,可以将一些临时变量通过自动释放池来回收统一释放。自动释放池本事销毁的时候,池子里面所有的对象都会做一次release操作 

创建一个自动释放池 @autoreleasepool { } 

2.autorelease

任何OC对象只要调用autorelease方法,就会把该对象放到离自己最近的自动释放池中(栈顶的释放池)。 

autorelease一般在对象出作用域时被调用,然后对象被加入到自动释放池,此时引用计数不变。

但是autorelease并不是根据作用域来决定释放时机的。那到底是依据什么呢?答案是:runloop。简单说,runloop就是iOS中的消息循环机制,当一个runloop结束时系统才会一次性清理掉被autorelease处理过的对象,其实本质上说是在本次runloop迭代结束时清理掉被本次迭代期间被放到autorelease pool中的对象的。至于何时runloop结束并没有固定的duration!

那么问题来了:iOS的这种基于runloop的内存回收策略有不方便的时候吗?显然有的。但凡事物总是有两面性的,使用autorelease的确方便,但在一定的情况下会带来性能问题。

3.总结

autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中; 自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(例如如果一个对象的引用计数器>1则此时就无法销毁); 

由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者考虑放到多个自动释放池; ObjC中类库中的静态方法一般都不需要手动释放,内部已经调用了autorelease方法;


三、属性参数以及的set方法(涉及内存)

propertyParameter

@property的参数分为三类,也就是说参数最多可以有三个,中间用逗号分隔,每类参数可以从上表三类参数中人选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)

一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高;readwirte方法会生成getter、setter两个方法,如果使用readonly则只生成getter方法;关于set方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:

assign,用于基本数据类型

-(void)setA:(int)a{    _a=a;}

retain,通常用于非字符串对象

-(void)setA:(Car *)a{    if(_a!=a){        [_a release];        _a=[a retain];    }}

copy,通常用于字符串对象、block、NSArray、NSDictionary

-(void)setA:(NSString *)a{    if(_a!=a){        [_a release];        _a=[a copy];    }}








0 0
原创粉丝点击