魏兆辉的IOS基础学习笔记之十二 OC语言基础-07 Foundation内存管理

本篇博文,将给大家介绍下再Objective-C中如何使用内存管理。一个程序运行的时候,如果不及时的释放没有用的空间内存。那么,程序会越来 越臃肿,内存占用量会不断升高。我们在使用的时候,就会感觉很卡,最终使得程序运行奔溃。因此,将无效的内存及时清理释放,是非常有必要的。

  一个对象在最初创建使用,到最后的回收释放,经历的是怎样一个过程呢?
包括:诞生(通过alloc或new方法实现)、生存(接收消息并执行操作)、交友(通过复合以及向方法传递参数)、最终死去(被释放掉)。

一、引用计数

  在对象创建的时候,Cocoa使用了一种叫引用计数的技术:
1)当一个对象被访问的时候,引用计数器的值就加1,可以给对象发送一条retain消息
2)当结束该对象的访问的时候,引用计数器的值就减1,可以给对象发送一条release消息
3)当引用计数器的值为0的时候,表示不再访问该对象,则其占用的系统内存将被回收重用,会自动给对象发送一条dealloc消息,一般都会重写dealloc方法;
4)要获得保留计数器当前的值,可以发送retainCount消息

  下面,介绍下几种方法的声明和实现方法:

首先,新建一个RetainTracker的类,修改类的声明文件和实现方法:

1 // RetainTracker.h
2
3 #import <Foundation/Foundation.h>
4
5 @interface RetainTracker : NSObject
6     -(id) retain;
7     -(oneway void)release;
8     -(NSUInteger)retainCount;
9 @end

 1 // RetainTracker.m
 2
 3 #import "RetainTracker.h"
 4
 5 @implementation RetainTracker
 6 -(id) init
 7 {
 8     if(self == [super init])
 9     {
10         NSLog(@"init: Retain count of %lu.", [self retainCount]);
11     }
12     return (self);
13 }
14
15 -(void) dealloc
16 {
17     NSLog(@"dealloc called. ByeBye!");
18     [super dealloc];
19 }
20
21 @end

然后在main.m主函数中调用retain,release,retainCount,dealloc等方法:

 1 #import <Foundation/Foundation.h>
 2 #import "RetainTracker.h"
 3
 4 int main(int argc, const char * argv[])
 5 {
 6     RetainTracker *tracker = [RetainTracker new];// count =1
 7
 8     [tracker retain];
 9     NSLog(@"retainCount: %lu", [tracker retainCount]);// count =2
10
11     [tracker retain];
12     NSLog(@"retainCount: %lu", [tracker retainCount]);// count =3
13
14     [tracker release];
15     NSLog(@"retainCount: %lu", [tracker retainCount]);// count =2
16
17     [tracker release];
18     NSLog(@"retainCount: %lu", [tracker retainCount]);// count =1
19
20     [tracker retain];
21     NSLog(@"retainCount: %lu", [tracker retainCount]);// count =2
22
23     [tracker release];
24     NSLog(@"retainCount: %lu", [tracker retainCount]);// count =1
25
26     [tracker release];// count =0, dealloc
27
28     return (0);
29 }

运行结果如下:

二、自动释放

  大家都知道,当对象不再使用的时候,要及时释放。但是在某些情况下,弄清楚什么时候不再使用一个对象并不容易。如果能够自动释放就好了。很幸运,Cocoa中有一个自动释放池(autorelease pool)。细心的朋友可以发现,在ios5以后,每次新建项目,在main函数中都有个@autoreleasepool方法,这就是将执行的代码都加到自动释放池中。

  NSObject类提供了一个叫做autorelease的方法:

1 -(id) autorelease;

  该方法预先设定一条会在未来某个时间发送的release消息。当给一个对象发送autorelease消息的时候,实际上是将该对象添加到自动释放池中。当自动释放池被销毁时,会向该池中的所有对象都发送release消息。

  那么,接下来,我们就用添加到自动释放池的方法来修改上面的例子。RetainCount类的内容不改变,只要修改main主函数中的内容:

 1 /* use auto release pool */
 2 int main(int argc, const char * argv[])
 3 {
 4     NSAutoreleasePool *pool;
 5     pool = [[NSAutoreleasePool alloc] init];
 6
 7     RetainTracker *tracker = [RetainTracker new];// count =1
 8     NSLog(@"after new, tracker: %lu", [tracker retainCount]);// count =1
 9
10     [tracker retain];
11     NSLog(@"after retain, tracker: %lu", [tracker retainCount]);// count =2
12     [tracker autorelease];
13     NSLog(@"after autorelease, tracker: %lu", [tracker retainCount]);// count =2
14
15     [tracker release];
16     NSLog(@"after release, tracker: %lu", [tracker retainCount]);// count =1
17     NSLog(@"releasing pool");
18     [pool release]; // 销毁自动释放池
19
20     @autoreleasepool {
21         RetainTracker *tracker2;
22         tracker2 = [RetainTracker new]; // count = 1
23         [tracker2 retain]; //count =2
24         [tracker2 autorelease]; // count still = 2
25         [tracker2 release]; //count = 1
26         NSLog(@"auto releasing pool.");
27     }
28
29     return (0);
30 }

  运行结果:

  tracker对象,通过autorelease消息,将该对象添加到自动释放池中。当pool自动释放池发送release消息的时 候,pool对象的引用计数器的值为0,则该自动释放池要被销毁,其dealloc方法被调用。使得自动释放池中的对象也都跟随其一起被销毁。

三、内存管理规则

  接下来,就给大家介绍下Cocoa的内存管理的几个规则。

1). 当你使用 new, alloc, copy 方法创建一个对象时,该对象保留计数器的值为1;
     当不使用的时候,要发送一条release或autorelease消息,销毁对象。

1 NSMutableArray *array;
2 array = [[NSMutableArray alloc] init]; //count =1
3 //use the array
4 [array release];//dealloc, count =0

2). 当你通过别的方法获得一个对象时,假设该对象的保留计数器的值为1,而已经被设置为自动释放,则不需要执行任何操作来清理该对象。

1 NSMutableArray *array;
2 array  =  [NSMutableArray arrayWithCapacity:17 ]; //count =1, autorelease;
3 //use the array

3). 自动释放池销毁的时间是完全确定的,它在循环阶段是不会被销毁的。如果一个循环要添加到自动释放池中的对象很多的时候,可以考虑循环一部分后先分批释放掉一些,然后再创建新的自动释放池。这样就保证自动释放池的分配和销毁操作代价尽可能的小。

 1 NSAutoreleasePool *pool;
 2 pool = [ [NSAutoreleasePool alloc] init ];
 3 int i;
 4 for(i=0; i<100000; i++)
 5 {
 6     id object = [someArray objectAtIndex: i];
 7     NSString *desc = [object descrption];
 8     If(i%100 == 0)
 9     {
10         // 每循环一百次就清空一次,然后新建一个自动释放池
11     [pool release];
12     pool = [[NSAutoreleaasePool alloc]init];
13 }
14 }
15 [pool release];

4). 自动引用计数(Auto Reference Counting,即:ARC),如果你启用了ARC,只要像平时一样按需分配并使用对象,编译器会帮你插入retain和release,无需你自己手动添加。ARC只能保留Objective-c的指针对象,即:继承NSObject的对象。

5). Ios 5 以上,有了归零弱引用,因为在指向的对象释放之后,这些弱引用就会被设置为零(即:nil),然后对象就会像平常指向nil值的指针一样被处理。使用前要先明确声明,声明方法如下:

1 _weak NSString *myString; 或
2 @property(weak) NSString *myString;
时间: 2024-08-01 10:43:10

魏兆辉的IOS基础学习笔记之十二 OC语言基础-07 Foundation内存管理的相关文章

魏兆辉的IOS基础学习笔记之十四 OC语言基础-09 OC对象的内存管理

一. 原理 对于任何继承了NSObject的对象,都有一个与之关联的整形变量,称为引用计数器:只有计数器的值为0的时候,OC就回收该对象,否则永不回收. 1. 创建对象时,如使用alloc ,new , copy,引用计数器的值为1 2. 当使用release方法释放时,计数器的值减1 3. 当使用retain方法时,计数器的值加1 4. 当对象被回收时候,自动调用对象的dealloc方法, 5. 使用retainCount方法来获取引用计数器的值 二. 防止内存泄露标准示例写法: @class

Java基础-学习笔记(十二)——抽象类和接口

1.抽象类的定义 java中可以定义一些不含任何语句的方法,它的方法体的实现方式可以交由子类来实现,这种方法叫抽象方法,只要类中有一个抽象方法,这个类就称为抽象类.含有抽象方法的类为抽象类,抽象类中所有的方法不一定全是抽象方法. 2.抽象类的规则 1)抽象类和抽象方法都需要用abstract关键字修饰 2)抽象类不能进行实例化,也就是不能用new创建抽象类的对象 3)抽象方法只需要声明,不用写语句 4)继承抽象类的子类只有在将从父类继承过来的所有抽象方法全部覆盖,该子类才能进行实例化,不然该子类

Python基础学习笔记(十二:文件基本操作)

SaltStack 学习笔记 - 第十二篇: SaltStack Web 界面

SaltStack 有自身的用python开发的web界面halite,好处是基于python,可以跟salt的api无缝配合,确定就比较明显,需要个性化对web界面进行定制的会比较麻烦,如果喜欢体验该界面的可以参考下面的文章  http://rfyiamcool.blog.51cto.com/1030776/1275443/ 我是运用另一个python+php来进行web开发,具体需要的工具有在我的另一篇文章里面介绍过,这里再重新进行整个开发介绍 首先介绍php 跟python通信的工具 pp

【Unity 3D】学习笔记四十二:粒子特效

粒子特效 粒子特效的原理是将若干粒子无规则的组合在一起,来模拟火焰,爆炸,水滴,雾气等效果.要使用粒子特效首先要创建,在hierarchy视图中点击create--particle system即可 粒子发射器 粒子发射器是用于设定粒子的发射属性,比如说粒子的大小,数量和速度等.在创建完粒子对象后,在右侧inspector视图中便可以看到所有的粒子属性: emit:是否是使用粒子发射器. min size:粒子最小尺寸. max size:粒子最大尺寸. min energy:粒子的最小生命周期

马哥学习笔记三十二——计算机及操作系统原理

缓存方式: 直接映射 N路关联 缓存策略: write through:通写 write back:回写 进程类别: 交互式进程(IO密集型) 批处理进程(CPU密集型) 实时进程(Real-time) CPU: 时间片长,优先级低IO:时间片短,优先级高 Linux优先级:priority 实时优先级: 1-99,数字越小,优先级越低 静态优先级:100-139,数据越小,优先级越高 实时优先级比静态优先级高 nice值:调整静态优先级   -20,19:100,139   0:120 ps

【Unity 3D】学习笔记三十二:游戏元素——游戏光源

游戏光源 在3D游戏中,光源是一个非常具有特色的游戏组件.用来提升游戏画面质感的.如果没有加入光源,游戏场景可能就会显得很昏暗.在unity中提供了三种不同的光源类型:点光源,聚光灯,平行光. 点光源 顾名思义,点光源是从一个点向周围散发出光的光源,就像电灯一样.创建点光源在hierarchy视图中点击create--point light: 创建完以后,点击点光源对象,在右侧inspector视图中可以看到点光源的所有信息: type:光源的类型.有point(点光源),directional

《Javascript权威指南》学习笔记之十二:数组、多维数组和符合数组(哈希映射)

Array(数组)是JavaScript中较为复杂的数据类型,同Java.C#.VB等程序语言的数组相比,Javascript数组中的元素不必为相同的数据类型,可以在数组每个元素上混合使用数字.日期.字符串.Object,甚至添加一个嵌套数组. 一.创建数组 1.var arr1 = new Array(); /var  arr2 = new Array(length); /var arr3 = new Array(element1,element2...); var arr4 = [eleme

汇编入门学习笔记 (十二)—— int指令、port

疯狂的暑假学习之  汇编入门学习笔记 (十二)--  int指令.port 參考: <汇编语言> 王爽 第13.14章 一.int指令 1. int指令引发的中断 int n指令,相当于引发一个n号中断. 运行过程相当于: (1)取中断类型吗n. (2)标志寄存器入栈:设置IF=0,TF=0. (3)CS.IP入栈 (4)(IP)=(n*4),(CS)=(n*4+2) 样例1:编写.安装中断7ch.实现求一个word型数据的平方,用ax存放这个数据. assume cs:code code s