浅谈 Objective-C 的内存管理

来源:互联网 发布:excel数据图表 编辑:程序博客网 时间:2024/05/16 17:50

       在学习 iOS 开发过程中,我们时常被对象的初始化和释放所困扰,由此引发软件性能的不稳定,crash 时有发生,但又不易排查。当你的程序创建出一个对象,对象会占内存,你要在对象不被使用后释放出内存空间。 也就是说,当对象不再需要时,要记着及时释放它。当然, 有时很难确定一个对象是否还会被使用, 比如 程序执行过程中,你的对象会被多个其他对象所引用,当被其他对象引用的可能性还存在时,就不能是否这个对象, 否则, 这样做可能会导致程序崩溃(crash)或出现不可预期的结果。

       为了帮助你处理那些不再被使用的对象, cocoa touch  为每个对象关联了一个计数器, 被称为 “保留计数器(retain counter)”。 当为对象增加一条引用信息时,就让对象在它的计数器里加一;当减少一次引用,则减一。 当保留计数器的计数为0时,对象就知道自己不再被引用了,此时可以被安全地毁掉了,这时候的对象会毁掉自己,并释放出内存空间。

     有了保留计数器,对象就知道自己被引用多少次。要使对象的保留计数器增加数值,要做的就是发送一条“保留信息(retain message)” ;减少计数器数值则要发送“释放信息(release message)”。 为确保对象的释放不被遗漏,建议你只要用到了 retain,一定要有对应的 release 操作。

    如此看来,维护 reference counting 是一笔不小的开销。 为解决这个问题, 苹果在Xcode 4.2 之后 (即 iOS 5版本) 推出了 ARC (Automatic Reference Counting)概念。 详见:  http://developer.apple.com/technologies/ios5/

Automatic Reference Counting
Automatic Reference Counting (ARC) for Objective-C makes memory management the job of the compiler. By enabling ARC with the new Apple LLVM compiler, you will never need to type retain or release again, dramatically simplifying the development process, while reducing crashes and memory leaks. The compiler has a complete understanding of your objects, and releases each object the instant it is no longer used, so apps run as fast as ever, with predictable, smooth performance.

对应中文如下:

自动引用计数

Objective-C 的自动引用计数(Automatic Reference Counting,ARC)使得内存管理成为编译器的工作。如果激活了新的 Apple LLVM 编译器的 ARC 功能,您将再也不用输入 retain 或者 release,极大地简化了开发过程,同时减少了程序崩溃和内存泄露的可能性。编译器了解对象的整个生命周期,并且在对象不再被使用的时候释放它,所以程序运行的和以前一样快,甚至有一些性能上的提高。

     需要说明的是: 如果是通过手工 alloc 的方式创建一个对象,在使用完后,需要用release 释放这个对象。 而通过自动释放(autorelease) 方式声明的对象, 便可不管。 若仍用 release 方法来释放, 这将使得应用程序崩溃。

//string1 将被自动释放 
        NSString* string1 = [NSString string];

release 和 autorelease 的区别, 标准的release会立刻释放对象的引用。autorelease会等一会儿才释放。
        //必须在用完后手工释放
       NSString* string2 = [[NSString alloc] init];
        .....

[string2 release];

在.m 文件中,经常看到如下的声明:

‐ (void) dealloc
      {
          [ var1 release];

    .......
          [super dealloc];
      }

通过 @interface 声明一个对象, 对象中又包含多个实例变量。 也就是说,实例变量是对象的成员。 当一个对象从内存中删除时,dealloc 方法将被调用。 这个方法要做的事情就是释放对象中的所有实例变量。 这里要特别注意 [super dealloc] 方法的调用。[super dealloc] 是向父类发送了一个消息,要求父类做清理工作。如果不这样做的话,该对象就不会从内存中删除,从而造成了内存泄露。

在实际应用中,通常只有两个原因,我们才会创建一个对象:

1. 作为一个实例变量保留;

2. 在函数内部作为临时变量使用。

对应第一种情况,只需要保证在dealloc 函数中释放(release)它就行了。 我们实际要做的是管理好函数内部的临时变量的引用,记住一个原则: 只要是通过alloc或copy 创建的对象,在函数结尾的地方,一定要发送一个 release 或autorelease 消息; 如果是通过其他方式创建的对象,就可以放手不管了。

 

继续实例说明:

 声明一个子函数, 将数字存储为字符串格式,如下:

- (NSString *)storedNumberAsString

{

NSString *stringToReturn = [[NSString alloc] initWithFormat:@"%f", storedNumber];

return stringToReturn;

}

乍一看,这个函数实现了我们想要的功能,但疏漏之处在于没有解决对象释放问题。通常,只要用到了alloc, 就应该有一个相应的 release。 修改如下,看看怎样:

- (NSString *)storedNumberAsString

{

NSString *stringToReturn = [[NSString alloc] initWithFormat:@"%f" , storedNumber];

[stringToReturn release];

return stringToReturn;

}

修改之后,的确实时释放了,释放的时序有误。在对象返回之前被释放,这意味着返回值已经没有了意义。

继续修改, 如下: 

- (NSString *)storedNumberAsString

{

NSString *stringToReturn = [[NSString alloc] initWithFormat:@"%f" , storedNumber];

return stringToReturn;

[stringToReturn release];  

}

修改之后, 先返回对象,紧接着释放对象,这在逻辑上有问题。 在同一个子函数中,返回之后的语句已经不再起作用了。 也就是说,这个释放形同虚设。

 我们在子函数中 分配并初始化了对象, 自然释放问题也应该由子函数完成,不能把这个“责任”推卸 子函数的调用者。 有一种方法可解决这个问题, 通过自动释放(autorelease)的方法。 修改如下: 

- (NSString *)storedNumberAsString

{

NSString *stringToReturn = [[NSString alloc] initWithFormat:@"%f" , storedNumber];

return [stringToReturn autorelease];

}

 

这里列举一个实例,在一个textview 中,将数字显示为字符串。 其实现函数如下:

- (IBAction)displaySomeText:(id)sender

{

WonderfulNumber *myWonderfulNumber = [[WonderfulNumber alloc] init];

[myWonderfulNumber setStoredNumber:pi];

NSString *numberString = [myWonderfulNumber storedNumberAsString];

 

[textView insertText:numberString];

[myWonderfulNumber release];

}

这里需要特别注意   [[WonderfulNumber alloc] init] 的init使用方法。在调试代码时,你会发现,即使省去init, 编译也没问题。 但从apple 资料中查知, init 还是需要的。 [[WonderfulNumber alloc]  这个方法是为对象分配了内存,紧接着,调用init 方法,对该对象进行初始化

0 0