内存管理

来源:互联网 发布:java类名是什么 编辑:程序博客网 时间:2024/05/20 03:37

内存管理在任何面向对象的语言里,譬如Objective-C,都是一个很重要的概念。ARC把几乎所有内存管理的事宜都转移给编译器来决定,使得开发者只需专注于业务逻辑。

理解引用计数

引用计数内存管理是基于一个计数器。当一个对象被创建,计数器至少为1,有人需要持有该对象时,计数加1,当完成时,计数减1,引用计数减为零时,该对象可被销毁。

用ARC简化引用计数

编译器Clang有一个静态分析器,可用于指出何处的引用计数出了问题(内存泄露),那么,再进一步的,也可以通过添加必要的retain或release,来防止内存泄露。这就是自动引用计数(ARC)的想法的来源。

事实上,ARC不使用一般Objective-C的消息派送机制,而是调用了更底层的C函数,以优化性能。因此,retain、release、autorelease方法从不直接调用,也不允许重载。

ARC使用方法名来表示内存的使用,如果一个方法以如下单词开头,那么该方法返回的对象由调用者所持有:

  • alloc
  • new
  • copy
  • mutableCopy

除了添加retain和release操作,ARC还能移除成对的、多余的retain、release、autorelease操作。譬如如下情形:

//Within EOCPerson class+ (EOCPerson *) personWithName:(NSString *)name {    EOCPerson *person = [[EOCPerson alloc] init];    person.name = name;    return person;//return [person autorelease];}//From a class where _myPerson is a strong instance variableEOCPerson *tmp = [[EOCPerson personWithName:"Bob"];//_myPerson = [tmp retain];

ARC在运行检测到了这一对多余的操作,通过调用函数objc_autoreleaseReturnValue 来代替直接调用autorelease方法,如若不久后对该对象使用了retain操作,则设置一个全局数据结构的标志位。同样的,retain操作也改为调用obje_retainAutoreleaseReturnValue,该函数监测那个标志位,如果被设置,则不执行retain操作。

变量的内存管理

默认的,对象对它的每一个变量都是强引用。对象的局部和实例变量,可使用如下修饰符修饰:

  • __strong:同retain
  • __unsafe_unretained:非持有关系,对象不置nil
  • __weak(Mac OS X 10.7 & iOS 5.0):非持有关系,对象释放后置nil
  • __autoreleasing:作为某方法的参数引用,返回时autorelease

dealloc只释放引用和解除监听

ARC能再dealloc方法中自动释放Objective-C的对象,然而,对于CoreFoundation的对象,以及分配在堆中的内存块malloc(),仍需要在dealloc方法中手动释放。譬如下面这样:

- (void) dealloc {   CFRelease(_coreFoundationObject);   free(_heapAllocatedMemoryBlob);}

dealloc方法中也无需调用[super dealloc],更不可覆写。

dealloc还可用于取消原来订阅的“键值观察”(KVO)或NSNotificationCenter的通知。

dealloc中要避免调用其他的方法,以免该方法使用的对象已被释放,或者需要运行在某个特定的线程上。dealloc一般在引用计数减为零的那个线程被调用。

dealloc也不可调用属性的存取方法。

另一方面,由于dealloc调用时机的不确定性,对于持有昂贵的或稀有的系统资源的对象,我们要在使用完毕后主动释放。考虑到容错,可在dealloc方法中加入如下代码来确保没有忘记释放这些对象:

- (void)close {  //clean up resources  _closed = YES;}- (void)dealloc {  if (!_closed) {      NSLog(@"ERROR: close was not called before dealloc!");      [self close];  }}

使用弱引用来避免循环引用

假设对象A拥有对象B,对象B也拥有对象A,当最后一个拥有对象A或B的对象被移除时,这个循环引用就形成内存泄露了。

unsafe_unretained也表达了一种非持有关系,但是只可用于对象,assign的语义与其类似,但是只可用于基本数据类型。ARC的运行时特性允许了安全的弱引用:当属性的值被解除分配,自动将该属性置为nil。

使用自动释放池来降低程序的内存峰值

释放一个对象意味着他的引用计数减1,或者被放入自动释放池。当这个池子排掉时,池子里的对象引用计数减1。自动释放池的释放时机是当前线程的下一个事件循环开始时。

如下代码可通过创建自动释放池来避免内存中存在大量的临时对象:

for (int i = 0; i < 100000; i++) {    [self doSomethingWithInt: i];}

当循环的长度是随意的、由用户的输入来确定时,更需要创建自动释放池。譬如下面这段从数据库中读取数据的代码片段:

NSArray *databaseRecords = /* ... */;NSMutableArray *people = [NSMutableArray new];for (NSDictionary *record in databaseRecords) {    @autoreleasepool {        EOCPerson *person = [[EOCPerson alloc] initWithRecord: record];        [people addObject: person];    }}

然而,创建自动释放池也是需要一定的开支的,所以使用前要先监控内存的使用情况,再慎重使用。

用僵尸变量调试内存管理的问题

可在Xcode的scheme editor中的诊断选项卡下,勾选“Enable Zombie Objects”,将解除分配的对象,转换为特殊的僵尸对象,且不回收内存空间。生成的僵尸对象的类为NSZombie_MYClass,这些僵尸类复制于_NSZombie的模板类,当有消息发送给僵尸对象时,就打印一条包含消息内容及其接受者的消息,然后终止应用程序。

Exception-safe代码下的内存管理

在ARC下使用Exception-safe代码存在发生内存泄露的问题,要避免发生内存泄露,要使用-fobjc-arc-exceptions这个编译标志,但这样做会加入大量样板代码,影响性能。

为何禁用retainCount

  1. 返回的引用计数只是某一个特定时间下的值;
  2. 返回的值可能永远不为零;
  3. 单例模式下,返回的值可能非常大;
  4. 返回的值可能没有你想象的那么精确,譬如当该对象存在于一个自动释放池中时。

参考文献:Effective Objective-C 2.0

0 0
原创粉丝点击