`
woodn_z
  • 浏览: 44977 次
  • 性别: Icon_minigender_1
  • 来自: 无锡
文章分类
社区版块
存档分类
最新评论

iphone开发 内存管理

阅读更多
关于所有权

所有权是iPhone内存管理的核心思想,对象的所有者负责在使用完对象后进行释放。一个对象可以有多个所有者,当它没有所有者时将被设置为取消分配(deallocation)。

创建对象时,所有权通过alloc、new、或者copy的方式建立,之后通过调用retain或者通过Cocoa函数来分配和复制对象的所有权。 内存释放有两种方式,一种方法是明确地请求释放对象的所有权,另一种方法则是使用自动释放池(auto-release pool)。

所有权的背后是一个和引用有关的运算系统,iPhone SDK的大多数对象使用这个系统,彼此之间建立着很强的引用和参照。

当你创建一个对象时,引用值为1,调用一次retain则对象的引用值加1,调用一次release则对象的引用值减1,当引用值为0时,对象的所有权分配将被取消。使用自动释放池意味着对象的所有权将在一段延后的时间内被自动取消。

对象之间也可以建立弱的引用参照,此时意味着,引用值不会被保留,对象的分配需要手动取消。

什么时候使用retain?

什么时候你想阻止对象在使用前就被释放?

每当使用copy、alloc、retain、或者Cocoa函数来创建和复制所有权,你都需要相应的release或者auto-release。

开发者应该从所有权的角度来考虑对象,而不必担心引用值。只要你有相应的retain和release方法,就能够对引用值进行+1和-1操作。

注意:你或许想使用[object retainCount],但它可能因为SDK的底层代码而发生返回值出错的情况。在内存管理时不推荐这种方式。
自动释放

将对象设置为自动释放意味着不需要明确地请求释放,因为当自动释放池清空时它们将被自动释放。iPhone在主线程上运行自动释放池,能够在事件循环结束后释放对象。当你创建你自己的线程时,你需要创建自己的自动释放池。

iPhone上有便利的构造函数,用这种方法创建的对象会设置为自动释放。

例子:

   1. NSString* str0 = @"hello"; 
   2. NSString* str1 = [NSString stringWithString:@"world"]; 
   3. NSString* str2 = str1;

一个已分配的对象可以用如下的方法设置为自动释放:

   1. NSString* str = [[NSString alloc] initWithString:@"the flash?"]; 
   2. [str autorelease];

或者用下面的方法:

   1. NSString* str = [[[NSString alloc] initWithString:@"batman!"] autorelease];

当指针出界,或者当自动释放池清空时,自动释放对象上的所有权将被取消。

在一个事件循环结束时,自动释放池内的构件通常会被清空。但是当你的循环每次迭代都分配大量内存时,你或许希望这不要发生。这种情况下,你可以在循 环内创建自动释放池。自动释放池可以嵌套,所以内部池清空时,其中分配的对象将被释放。在下面的例子中,每次迭代后将释放对象。

   1. for (int i = 0; i < 10; ++i) 
   2. { 
   3.         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
   4.         NSString* str = [NSString stringWithString:@"hello world"]; 
   5.         [self ProcessMessage: str]; 
   6.         [pool drain]; 
   7. }

注意:在编写的时候iPhone不支持垃圾回收,所以drain和release的功能相同。当你想为程序设置OSX的端口时通常会使用drain,除非后来在iPhone中添加了垃圾回收机制。Drain能够击发垃圾回收器释放内存。
返回一个对象的指针

开发者在遵循所有权规则时需要清楚哪些函数拥有对象的所有权。下面是返回一个对象的指针并释放的例子。

错误的方法:

   1. - (NSMutableString*) GetOutput 
   2. { 
   3.         NSMutableString* output = [[NSMutableString alloc] initWithString:@"output"]; 
   4.         return output; 
   5. } 
   6. - (void) Test 
   7. { 
   8.         NSMutableString* obj = [self GetOutput]; 
   9.         NSLog(@"count: %d", [obj retainCount]); 
  10.         [obj release]; 
  11. }

在这个例子中,output 的所有者是 GetOutput,让 Test 释放 obj 违反了Coccoa内存管理指南中的规则,尽管它不会泄露内存但是这样做不好,因为Test 不应该释放并非它所拥有的对象。

正确的方法:

   1. - (NSMutableString*) GetOutput 
   2. { 
   3.         NSMutableString* output = [[NSMutableString alloc] initWithString:@"output"]; 
   4.         return [output autorelease]; 
   5. } 
   6. - (void) Test 
   7. { 
   8.         NSMutableString* obj = [self GetOutput]; 
   9.         NSLog(@"count: %d", [obj retainCount]); 
  10. }

在第二个例子中,output 被设置为当 GetOutput 返回时自动释放。output的引用值减少,GetObject 释放 output 的所有权。Test 函数现在可以自由的 retain 和 release 对象,请确保它不会泄露内存。

例子中 obj 被设置为自动释放,所以 Test 函数没有它的所有权,但是如果它需要在其他地方存储对象会怎样?

此时对象需要有一个新的所有者来保留。
Setters

setter函数必须保留它所存储的对象,也就是声明所有权。如果我们想要创建一个 setter 函数,我们需要在分配一个新的指向成员变量的指针之前做两件事情。

在函数里:

   1. - (void) setName:(NSString*)newName

首先我们要减少成员变量的引用值:

   1. [name release];

这将允许当引用值为0时 name 对象被释放,但是它也允许对象的其他所有者继续使用对象。

然后我们增加新的 NSString 对象的引用值:

   1. [newName retain];

所以当 setName 结束时, newName 不会被取消分配。 newName 现在指向的对象和 name 指向的对象不同,两者有不同的引用值。

现在我们设置 name 指向 newName 对象:

   1. name = newName;

但是如果 name 和 newName 是同一个对象时怎么办?我们不能在它被释放后保留它,并再次释放。

在释放存储的对象前保留新的对象:

   1. [newName retain]; 
   2. [name release]; 
   3. name = newName;

现在两个对象是相同的,先增加它的引用值,然后再减少,从而使得赋值前引用值不变。

另一种做法是使用 objective-c:

声明如下:

   1. @property(nonatomic, retain) NSString *name;

1. nonatomic 表示没有对同一时间获取数据的多个线程进行组块儿。Atomic 为一个单一的线程锁定数据,但因为 atomic 的方式比较缓慢,所以不是必须的情况一般不使用。

2. retain 表示我们想要保留 newName 对象。

我们可以使用 copy 代替 retain:

   1. @property(nonatomic, copy) NSString *name;

这和下面的函数一样:

   1. - (void) setName:(NSString*)newName 
   2. { 
   3.         NSString* copiedName = [newName copy]; 
   4.         [name release]; 
   5.         name = copiedName; 
   6.         [name retain]; 
   7.         [copiedName release]; 
   8. }

newName 在这里被复制到 copiedName,现在 copiedName 拥有串的一个副本。name 被释放,而 copiedName 被赋给 name。之后 name 保留这个串,从而使得 copiedName 和 name 同时拥有它。最后 copiedName 释放这个对象,name 成为这个串的唯一所有者。

如果我们有如下的函数,像这样的 setters 将被输入用来保留成员对象:

   1. - (void) Test 
   2. { 
   3.    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
   4.    // do something... 
   5.    name = [self GetOutput]; 
   6.    // do something else... 
   7.    NSLog(@"Client Name before drain: %@", name); 
   8.    [pool drain]; 
   9.    NSLog(@"Client Name after drain: %@", name); 
  10. }

name 在调用至 drain 后是未定义的,因为当池被释放时,name 也将被释放。

如果我们用如下的部分替代赋值:

   1. [self setName:[self GetOutput]];

然后 name 将被这个类所有,在使用时保留直到调用 release

那么我们何时释放对象?

由于 name 是成员变量,释放它的最安全的办法是对它所属的类使用 dealloc 函数。

   1. - (void)dealloc 
   2. { 
   3.    [name release]; 
   4.    [super dealloc]; 
   5. }

注意:虽然并不总是调用 dealloc,依靠 dealloc 来释放对象可能是危险,可能会触发一些想不到的事情。在出口处,iPhone OS 可能在调用 dealloc 前清空全部应用程序的内存。

当用 setter 给对象赋值时,请小心下面的语句:

   1. [self setName:[[NSString alloc] init]];

name 的设置是正确的但 alloc 没有相应的释放,下面的方式要好一些:

   1. NSString* s = [[NSString alloc] init]; 
   2. [self setName:s]; 
   3. [s release];

或者使用自动释放:

   1. [self setName:[[[NSString alloc] init] autorelease]];

自动释放池

自动释放池释放位于分配和 drain 函数之间的对象。

我们在下面的函数中设置一个循环,在循环中将 NSNumber 的一个副本赋给 magicNumber,另外将 magicNumber 设置为自动释放。在这个例子中,我们希望在每次迭代时清空自动释放池(这样可以在赋值的数量很大时节省循环的内存)

   1. - (void) Test 
   2. { 
   3.    NSString* clientName = nil; 
   4.    NSNumber* magicNumber = nil; 
   5.    for (int i = 0; i < 10; ++i) 
   6.    { 
   7.            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
   8.            magicNumber = [[self GetMagicNumber] copy]; 
   9.            [magicNumber autorelease]; 
  10.            if (i == [magicNumber intValue]) 
  11.            { 
  12.                    clientName = [self GetOutput]; 
  13.            } 
  14.            [pool drain]; 
  15.    } 
  16.    if (clientName != nil) 
  17.    { 
  18.            NSLog(@"Client Name: %@", clientName); 
  19.    } 
  20. } 
  21. 

这里存在的问题是 clientName 在本地的自动释放池中被赋值和释放,所以当外部的池清空时,clientName 已经被释放了,任何对 clientName 的进一步使用都是没有定义的。

在这个例子中,我们在赋值后保留 clientName,直到结束时再释放它:

   1. - (void) Test 
   2. { 
   3.    NSString* clientName = nil; 
   4.    NSNumber* magicNumber = nil; 
   5.    for (int i = 0; i < 10; ++i) 
   6.    { 
   7.            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
   8.            magicNumber = [[self GetMagicNumber] copy]; 
   9.            [magicNumber autorelease]; 
  10.            if (i == [magicNumber intValue]) 
  11.            { 
  12.                    clientName = [self GetOutput]; 
  13.                    [clientName retain]; 
  14.            } 
  15.            [pool drain]; 
  16.    } 
  17.    if (clientName != nil) 
  18.    { 
  19.            NSLog(@"Client Name: %@", clientName); 
  20.            [clientName release]; 
  21.    } 
  22. }

我们在调用 retain 函数和 release 函数的期间获得 clientName 的所有权。通过添加一对 retain 和 release 的调用,我们就确保 clientName 在明确调用释放前不会被自动释放。
集合

当一个对象被添加进集合时,它就被集合所拥有。

在这个例子中我们分配一个串,它现在有了所有者;

   1. NSString* str = [[NSString alloc] initWithString:@"Bruce Wayne"];

然后我们将它添加进数组,现在它有两个所有者:

   1. [array addObject: str];

我们可以安全的释放这个串,使其仅被数组所有:

   1. [str release];

当一个集合被释放时,其中的所有对象都将被释放。

   1. NSMutableArray* array = [[NSMutableArray alloc] init]; 
   2. NSString* str = [[NSString alloc] initWithString:@"Bruce Wayne"]; 
   3. [array addObject: str]; 
   4. [array release];

在上面的例子中,我们分配了一个数组和一个串,然后将串添加到数组中并释放数组。这使得串仅拥有一个所有者,并且在我们调用 [str release] 前它不会被释放。
用线程传递指针

在这个函数中,我们从串的 input 传递到函数 DoSomething,然后释放 input

   1. - (void) Test 
   2. { 
   3.    NSMutableString* input = [[NSMutableString alloc] initWithString:@"batman!"]; 
   4.    [NSThread detachNewThreadSelector:@selector(DoSomething:) toTarget:self withObject:input]; 
   5.    [input release]; 
   6. }

detatchNewThreadSelector 增加 input 对象的引用值并在线程结束时释放它。这就是为什么我们能够在线程刚开始的时候就释放 input,而无论函数 DoSomething 何时开始或结束。

   1. - (void) DoSomething:(NSString*)str 
   2. { 
   3.    [self performSelectorOnMainThread:@selector(FinishSomething:) withObject:str waitUntilDone:false]; 
   4. }

performSeclectorOnMainThread 也会保留传递的对象,直到 selector 结束。

自动释放池是特殊的线程,所以如果我们在一个新的线程上创建自动释放的对象,我们需要创建一个自动释放池来释放它们。

   1. [NSThread detachNewThreadSelector:@selector(Process) toTarget:self withObject:nil];

这里在另一个线程上调用函数 Process

   1. - (void) Process 
   2. { 
   3.    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
   4.    NSMutableString* output = [[[NSMutableString alloc] initWithString:@"batman!"] autorelease]; 
   5.    NSLog(@"output: %@", output); 
   6.    [self performSelectorOnMainThread:@selector(FinishProcess) withObject:nil waitUntilDone:false]; 
   7.    [pool drain]; 
   8. }

对象 output 被分配并且在自动释放池中设置了自动释放,它将在函数结束前被释放。

   1. - (void) FinishProcess 
   2. { 
   3.    NSMutableString* output = [[[NSMutableString alloc] initWithString:@"superman?"] autorelease]; 
   4.    NSLog(@"output: %@", output); 
   5. }

系统会为主线程自动创建一个自动释放池,所以在 FinishProcess 中,我们不需要为主线程上运行的函数创建自动释放池。
总结

为了在你的iPhone中避免内存泄露,你必须要清楚每个被分配对象的所有者是谁,要明白什么时候释放所有权,并且还要始终按对设置 retain 和 release,这三点非常重要。如果你遵循所有权的规则,你的应用将更加稳定并且因为 bug 的减少而节省大量时间。
分享到:
评论

相关推荐

    ios iphone开发-内存管理

    ios iphone开发-内存管理 所有权是iPhone内存管理的核心思想,对象的所有者负责在使用完对象后进行释放。一个对象可以有多个所有者,当它没有所有者时将被设置为取消分配(deallocation)

    iphone开发官方指南-内存管理编程指南

    ### iPhone开发官方指南:内存管理编程指南精要 #### 引言 在软件工程领域,尤其是在移动应用开发中,如iPhone应用开发,内存管理是一项核心技能。它关乎应用的性能、稳定性和用户体验。《iPhone开发官方指南:...

    iPhone内存管理

    本文将深入探讨iPhone开发中Objective-C的内存管理技术,涵盖基本概念、核心原则以及实战技巧。 #### 一、Objective-C与内存管理 Objective-C作为苹果开发环境的主要语言之一,其内存管理机制区别于自动管理的.NET...

    iPhone开发教程之iPhone内存释放注意事项两则--千锋培训

    ### iPhone开发教程之iPhone内存释放注意事项两则 #### 内存管理基础知识 在iOS开发过程中,内存管理是一项非常重要的任务。对于iOS应用来说,如果内存管理不当,可能会导致程序崩溃或者被系统强制关闭。在...

    iPhone 内存管理

    在探讨iPhone内存管理的核心知识点时,我们主要关注的是Objective-C编程环境下如何有效地管理和优化iOS设备上的内存使用。尽管部分信息因版权原因无法详尽展示,但我们可以基于标题、描述、标签以及部分内容,深入...

    iPhone 应用开发中Object-C 内存管理--千锋培训

    iPhone应用开发中的Object-C内存管理是开发者必须掌握的关键技能,特别是在资源有限的移动设备上,如iPhone OS(现称为iOS)。内存管理不当可能导致程序崩溃,严重影响用户体验。Object-C的内存管理基于引用计数机制...

    如何学好Iphone开发

    学习iPhone开发是一个系统而全面的过程,它涉及到编程基础、开发工具、框架掌握等多个方面。文章以《如何学好iPhone开发》为主题,详细阐述了学习iPhone游戏开发所需的基础知识、推荐的学习资源、开发工具的运用、...

    iPhone开发基础教程-PDF版

    除此之外,你还会接触到iOS的生命周期管理,包括应用的启动过程、状态管理和内存管理。了解如何妥善处理后台任务和节能模式,确保应用在不同场景下都能正常运行。 在导航和数据存储方面,教程会讲解UITableView和...

    深入浅出iPhone开发

    这包括测试策略、内存管理、性能分析工具的使用,以及App Store的提交流程和规定。 总之,《深入浅出iPhone开发》是一本全面覆盖iPhone应用开发的教程,它不仅教授技术,更注重实践,让读者通过实例学习,从而真正...

    iPhone应用程序开发指南.pdf

    同时,对性能和内存管理的要求也很高,开发者必须确保应用程序能够高效运行,不会消耗过多的内存或电池。 iPhone应用开发指南还强调了开发过程中所需的各类文档和资料,包括API参考、编程指南以及各种开发工具和...

    iPhone开发揭秘.pdf

    本书《iPhone开发揭秘》是一本基础的iPhone开发教程,主要围绕非游戏框架的iPhone程序开发进行介绍。非游戏框架基于iPhone用户界面的标准控件,即典型的iPhone程序包含一个Window和若干个UIViewController,每个...

    iPhone开发秘籍(第2版)

    《iPhone开发秘籍(第2版)》是一本专为iOS开发者量身打造的专业指南,旨在帮助读者深入理解和掌握iPhone应用程序的开发技术。这本教材的出现,为想要涉足或已经在iOS开发领域摸爬滚打的程序员提供了丰富的知识库,...

    iPhone开发实战

    11. **测试与性能优化**:涵盖单元测试、UI测试,以及内存管理、性能分析和优化技巧。 12. **App Store发布**:包括应用程序的签名、打包、提交审核和版本管理过程。 13. **响应式编程**:SwiftUI的引入使得iOS...

    iphone内存管理

    《iPhone内存管理详解》 Objective-C作为iOS应用开发的主要编程语言,其内存管理机制对于开发者来说至关重要。本文将深入探讨Objective-C中的内存管理基础,帮助初学者理解和掌握如何有效防止内存泄漏和程序崩溃。 ...

    iphone开发教程2

    开发者需要学习如何利用Swift的特性,如类型安全、自动内存管理、闭包以及强大的Playgrounds来快速原型设计。同时,理解Apple的开发框架,如Core Animation、Core Location、Core Data等,也是成功开发iPhone应用的...

    [深入浅出iPhone开发(中文版)]

    iOS是基于Darwin(Unix衍生版)的操作系统,其核心服务包括多任务处理、内存管理、安全机制以及网络支持。UIKit框架是iOS应用程序的主要构建块,提供了用户界面元素和事件处理。 二、Objective-C语言 Objective-C是...

    iphone开发教程11

    此外,可能还会涉及性能优化、内存管理以及测试策略等实践技巧。 总的来说,“iPhone开发教程11”是一份全面的教育资源,涵盖了从基础编程到高级应用开发的多个层次,适合初学者和有一定经验的开发者。通过学习这个...

    iPhone开发快速入门简介

    Cocoa框架包括Foundation和UIKit,前者负责对象管理、内存管理和数据结构操作,后者则提供窗口、视图、控件等可视化元素的底层构架。 MVC(Model-View-Controller)模式在iPhone开发中扮演关键角色,Model管理数据...

    iPhone 开发例子2

    在本主题"iPhone 开发例子2"中,我们将深入探讨iPhone应用开发的相关知识,这个压缩包文件包含了2008年11月19日的示例代码,旨在为正在学习iPhone开发的初学者提供实践指导。以下是这些示例可能涵盖的一些关键知识点...

    iphone开发教程13

    开发过程涵盖了界面设计、功能实现、性能优化、内存管理、网络通信等多个方面。Swift是一种现代、安全且高效的编程语言,它的易用性和强大的功能使得初学者和经验丰富的开发者都能快速上手。 【压缩包子文件的文件...

Global site tag (gtag.js) - Google Analytics