垃圾回收:
垃圾回收指的是程序运行过程中,检查是否有不再使用的对象,并自动释放他们所占用的内存,通常被简称为GC。内存的检查和回收都是由垃圾收集器完成的。
在OC2.0中,垃圾回收首先进行的工作就是识别不允许被回收的对象。全局变量,静态变量和栈内临时变量他们所引用的对象不允许被回收。这些对象称为根集合。如果一个对象的实例变量引用了不允许被回收的对象或根集合中的对象,那么这个对象也不允许被回收。也就是说,通过全局变量,静态变量或者栈内变量的强引用而查找到的对象都不可以被回收。反之,只要是从根集合出发无法到达的对象都属于垃圾回收的目标。
对于不再使用的对象我们可以通过为其赋值nil来明确表示其已不再被使用,据此通知垃圾收集器回收该对象。这种做法不仅有助于垃圾回收,也是一种良好的编程风格。
垃圾收集器整个回收过程完全是根据程序占用内存的多少自动完成的。一个程序只有一个垃圾收集器,通常在mainthread中运行。它通常作为一个单独的线程运行。垃圾收集器运行的时候有可能会暂停其他线程来完成垃圾收集。
主动启动垃圾回收:通过给类NSGarbageCollector发送collectIfNeeded消息来启动垃圾收集器。通过主动启动垃圾回收,不仅可以控制内存消费,又可以避免垃圾回收突然启动时程序反应变慢的问题。
finalize方法的定义:
finalize方法被定义在NSObject中,在对象被回收之前,finalize方法会被执行。原则上不推荐轻易定义finalize,因为finalize方法作为垃圾回收的一部分被执行,垃圾回收在一个独立的线程中进行。同软件本身是并行关系,如果finalize处理过于复杂的话就会影响软件本身的执行速度。另外,要被释放的对象的finalize方法的执行是没有先后顺序的。不要依赖finalize方法的执行顺序来做任何事情。
实现finalize方法的时候要注意下面这些事项:
finalize方法内不用担心其他对象的释放。
要注意finalize方法中对象的赋值。
finalize的执行顺序和对象的释放顺序都是无法确定的。
finalize不是线程安全的。
对象的善后处理应该在这个对象不再使用的时候进行,而不要在finalize 方法中进行。特别是文件之类的资源对象,不要在finalize方法中进行关闭处理。进行善后处理之后,可以将不再使用的对象赋值为别的对象或者nil,以取消对该对象的引用,并告知垃圾收集器该对象可以被清理了。
反之,在多个地方释放对象时,就不得不用到finalize方法。
使用垃圾回收编程小结:
·编译的时候需要使用编译选项fobjc-gc-only
·使用局部变量给方法中的局部对象赋值
·如果暂时不想垃圾回收某对象,需要保证该对象能够被全局变量引用
·原则上垃圾回收只负责回收id类型或类类型的变量
·不用考虑所有权的问题,也不用考虑实例变量的问题
·释放对象时的一些“善后”操作可以写在finalize方法中,但除了不写在finalize中就无法实
现的功能,要尽量减少通过finalize完成的操作
·引用计数相关的方法和dealloc方法不会被执行
分代垃圾回收:
启用垃圾回收后,会在变量赋值,改写变量时建立写屏障。OC2.0利用修改变量时的信息采用分代垃圾回收的方式来进行内存管理。分代垃圾回收并不会每次对整个堆空间进行遍历,而是以新生成的对象为中心,以尽可能快速的收集那些生命周期短的对象,通过这种分代的方法,既可以减轻垃圾回收的处理负荷,又能有效释放空间,但也要小心这种方法会积攒太多的陈旧对象。
弱引用:
和ARC不一样的是,栈内的临时变量不能声明为弱引用。同时也可以使用_ _ strong修饰符来声明一个变量是强引用类型。除了这两个修饰符外,_ _autoreleasing和_ _unsafe_unretained等其他生命期修饰符在垃圾回收中都不可用。
弱引用的作用在于解决强引用所带来的对象之间在存活时间上的耦合关系。
尽量不要使用这种依赖于垃圾回收的运行频率和时机的编程方法。
通过垃圾回收回收动态分配的内存:
通过NSAllocateCollectable()分配的内存因为可以通过垃圾回收被自动回收,所以可以不考虑内存释放的问题,比使用malloc和free编程更方便。但指向初对象之外的动态内存的指针变量都需要用_ _strong来修饰。另外,程序的效率有可能会降低,所以并不需要特意把原来使用的malloc/free的代码都改为NSAllocateCollectable。
_ _ strong修饰符的使用方法:
修饰对象类型的变量。
修饰指针类型的变量。被_ _strong修饰的指针所指向的内存可以被垃圾回收,但要等到该指针不再使用为止。
用于修饰函数或方法返回的指针类型。指针指向的内存属于垃圾回收的范畴,但如果被赋值给一个强引用类型的变量,就暂时不能被回收。
NSGarbageCollector类:
+ (id)defaultCollector //返回现在有效的垃圾收集器的实例,如果垃圾收集无效,则返回nil。
- (void)collectIfNeeded //申请进行垃圾回收。这个方法被调用之后会判断当前内存使用情况,如果觉得需要进行垃圾回收就会启动垃圾收集器。典型的调用方式如下:
[[NSGarbageCollectordefaultCollector]collectIfNeeded];
- (void)collectExhaustively //申请进行垃圾回收。希望使用深度遍历以尽可能地释放更多的内存时使用。这个方法被调用之后会判断当前内存使用情况,如果觉得需要进行垃圾回收就会启动垃圾收集器。
- (void)disableCollectorForPointer:(void *)ptr //调用这个函数之后,这个指针所指向的内存不会被回收。也就是说指针指向的对象会变成根集合中的对象。如果想让对象变为可以被再次回收的话,需要调用下面的enableCollectorForPointer:
- (void)enableCollectorForPointer:(void *)ptr //能够让通过disableCollectorForPointer:指定不允许被释放的内存或对象再次允许被释放。
- (void)enable
- (void)disable //disable方法能让垃圾收集器暂时停止内存回收。enable方法则能让垃圾收集器恢复工作。但enable和disable不许成对使用。这两个函数一般被用于防止多线程中同时运行垃圾回收。
- (BOOL)isEnabled //用于判断垃圾回收器是否有效,有效时返回真。
实时API:
控制垃圾回收器的动作。
void objc_startCollectorThread (void); //启动一个线程专门运行垃圾收集器。
oid *objc_memmove_collectable(void *dst, const void *src, size_t size); //复制垃圾回收对象的内存时使用该函数替代memcpy和memmove。
内存管理方式的比较:
首先让我们来看看和手动内存管理相比,ARC和垃圾回收有哪些优点。
·不需要再在意对象的所有权
·可以删除程序中内存管理部分的大部分代码,使程序看起来更清爽
·可以避免手动内存管理时的错误(内存泄漏等)
·不需要再在意引用计数内存管理中的一些特殊用法。例如访问方法的定义和临时对象的使用等
·可以使多线程环境下的编程更简单。例如:不用担心不同的线程之间可能出现的所有权冲突
垃圾回收的缺点如下所示。
·垃圾收集器运行时会影响程序的速度
·需要不停地监视内存的使用,同引用计数的方法相比,程序的速度会变慢
·会影响程序的效率。不经常使用的内存也会被垃圾收集器不时地访问,实际上有可能会占用
更多的内存
·需要使用一些技巧来让对象被回收或不被回收
·无法使用引用计数管理方式下的一些设计方针。例如:事先准备好一些管理文件或其他资源类的对象,在对象被释放的同时关闭资源
相比较而言,ARC没有垃圾回收那么多缺点,但也有一些要注意的地方
·循环引用一旦形成就不会自己消失
·为了防止循环引用的形成,需要注意对象间的关系。GUI的各个类之间也有可能形成循环引
用,也要注意防止循环引用的形成
·理论上可被赋值的对象都可以被自动释放,但在处理结构体、数组、二重指针等类型的变量
时有一些限制
·需要注意Core Foundation的对象和Objectve-G的对象之间的转换(详情请参考附录B)
更改内存管理方式:
与对象的所有权和生存期相关的处理
释放对象之后的善后处理
防止野指针的生成
有时还需要从对象之间的相互关系来重新考虑