• Objective-C为每个对象提供一个内部计数器,这个计数器跟踪对象的引用次数。所有类都继承自 NSObject 的对象retain和release方法。

如果使用了new、alloc或copy方法获得一个对象,则我必须释放(release)或自动释放(autorelease)该对象

复制分为浅层复制(指向同一个地址)和深层复制(创建不同的实例)。

• 当对象被创建或拷贝时候,引用计数为1。每次保持对象时候,就发送一条retain消息,使其引用计数加1,如果不需要这个对象就是发送一个release消息使其引用计数减1。当对象的引用计数为0的时候,系统就知道不再需要这个对象了,就会释放它内存。

• 一个对象的创建可以通过alloc分配内存或copy复制(深层复制),这关系到的方法有:alloc, allocWithZone:, copy,copyWithZone:, mutableCopy(可变对象),mutableCopyWithZone:,这些方法都可以使引用计数为1,retain会使引用计数加1,release会使引用计数减1。

当对象包含其它对象时,就得在 dealloc中自己释放它们。

@interface Song : NSObject {
NSString *title;
NSString *artist;
long int duration;
}
//操作方法
- (void)start;
- (void)stop;
- (void)seek:(long int)time;
- //访问成员变量方法
@property NSString *title;
@property NSString *artist;
@property(readwrite) long int duration;
//构造方法
-(Song*) initWithTitle: (NSString *) title andArtist:
(NSString *) artist
andDuration:( long int )duration ;
@end

 重写dealloc

#import "Song.h"

@implementation Song

@synthesize title;
@synthesize artist;
@synthesize duration; //构造函数
-(Song*) initWithTitle: (NSString *) newTitle andArtist: (NSString *) newArtist andDuration:(long int)newDuration {
self = [super init];
if ( self ) {
self.title = newTitle;
self.artist = newArtist;
self.duration = newDuration;
}
return self; } - (void)start {
//开始播放
} - (void)stop {
//停止播放
} - (void)seek:(long int)time {
//跳过时间
} -(void) dealloc {
NSLog(@"释放Song对象..."
);
[title release];
[artist release];
[super dealloc];
}

@end
#import <Foundation/Foundation.h>
#import "Song.h"
int main (int argc, const char * argv[]) {
Song *song1 = [[Song alloc] initWithTitle:@"Big Big World"
andArtist:@"奥斯卡.艾美莉亚" andDuration:];
Song *song2 = [[Song alloc] initWithTitle:@"It's ok"
andArtist:@"atomic kitten" andDuration:];
// print current counts
NSLog(@"song 1 retain count: %i", [song1 retainCount] );
NSLog(@"song 2 retain count: %i", [song2 retainCount] );
// increment them
[song1 retain]; //
[song1 retain]; //
[song2 retain]; // 2
// print current counts
NSLog(@"song 1 retain count: %i", [song1 retainCount] );
NSLog(@"song 2 retain count: %i", [song2 retainCount] );
... ...
// decrement
[song1 release]; //
[song2 release]; // 1
// print current counts
NSLog(@"song 1 retain count: %i", [song1 retainCount] );
NSLog(@"song 2 retain count: %i", [song2 retainCount] );
// release them until they dealloc themselves
[song1 release]; //
[song1 release]; //
[song2 release]; //
return ;
}

• 在这个main函数中,声明了两个Song对象,当retain调用增加引用计数,而release调用减少它。调用[objretainCount] 来取得引用计数的 int 值。 当retainCount到达 0,两个对象都会调用dealloc,所以可以看到印出了两个 “释放Song对象...”。在Song对象释放的时候,先要释放它自己的对象类型成员变量title和artist,然后再调用[super dealloc]

内存释放池(Autorelease pool)提供了一个对象容器(通过类方法创建的对象不能release,而要通过pool释放),每次对象发送autorelease消息时,对象的引用计数并不真正变化,而是向内存释放池中添加一条记录,记下对象的这种要求,直到当内存释放池发送drain或release消息时,当池被销毁前会通知池中的所有对象,全部发送release消息真正将引用计数减少。

垃圾收集混合环境下:应该使用drain方法,因为release在GC模式下没有意义

• 这些语句必须要放在下面语句之间,直到池被释放,一个对象要想纳入内存释放池对象,必须要发送autorelease

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
… …
[pool release];// [pool drain];
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *weeksNames1 = [NSArray arrayWithObjects:
@"星期一",@"星期二",@"星期三",@"星期四"
,@"星期五",@"星期六",@"星期日",nil];
NSArray *weeksNames2 = [[NSArray alloc] initWithObjects:
@"星期一",@"星期二",@"星期三",@"星期四"
,@"星期五",@"星期六",@"星期日",nil];
//[weeksNames1 release];
//[weeksNames1 autorelease];
//[weeksNames2 release];
//[weeksNames2 autorelease];
NSLog(@" retain count: %i" , [weeksNames1 retainCount] );
NSLog(@" retain count: %i" , [weeksNames2 retainCount] );
[pool release];
return ;
}

NSArray类是Foundation框架提供的不可变数组类,Foundation框架中对象的创建有两类方法:类方法(+)构造方法和实例方法(-)构造方法。打开NSArray ClassReference文档,其中创建对象有关的方法如图所示。

从NSArray Class Reference文档可以看出,以+和类名开头(去掉NS,小写第1个字母,array),就是类级构造方法,以-和initWith开头的就是实例构造方法。类级构造方法不能使用release,可以不用autorelease就可以自动纳入内存释放池管理。实例构造方法,如果发出release消息就马上释放对象,如果发出autorelease消息可以自动纳入内存释放池管理,不会马上释放。在iOS开发中由于内存相对少,因此基本上都采用实例构造方法实例化对象,采用发送release消息立刻释放对象内存。

有的时候我们声明的对象要跨越对象调用,内存管理就会变的更加复杂。例如在Song类中有一对成员变量存取的方法,当然可以把它们封装成属性,通过属性参数来管理内存。在Song类中给成员变量设置方法如下:

- (void)setTitle:(NSString *)newTitle {
title = newTitle;
}
- (void)setArtist:(NSString *)newArtist {
artist = newArtist;
}

这段代码事实上有内存泄漏,当设置一个新的Title时候,title = newTitle只是将指针改变了(把新对象的指针赋给了旧对象), 旧对象并没有释放,所以我们会这样修改这些方法:

- (void)setTitle:(NSString *)newTitle {
[newTitle retain];//保持,防止释放
[title release];//把旧的释放
title
= [[NSString alloc] initWithString: newTitle];//给新对象重新分配内存创建对象
[newTitle release];//释放传递进来的对象
}
- (void)setArtist:(NSString *)newArtist {
[newArtist retain];
[artist release];
artist
= [[NSString alloc] initWithString: newArtist];
[newArtist release];
 }

首先保留新对象,释放旧对象,然后使用实例构造方法实例化新的对象。参数newTitle不要在方法中释放。由于基本数据类型(非对象类型)不需要释放,因此下面的写法是没有问题的。

- (void)setDuration:(long int)newDuration {
duration = newDuration;
}

此外,在构造方法中也必须要注意,不能直接赋值title =newTitle,而是要调用自身的设置方法

//构造方法
-(Song*) initWithTitle: (NSString *) newTitle andArtist:
(NSString *) newArtist
andDuration:(long int)newDuration {
self = [super init];
if ( self ) {
[self setTitle:newTitle];
[self setArtist:newArtist];
[self setDuration:newDuration];
}
return self;
}

assign参数代表设置时候直接赋值(用于基本数据类型),而不是复制或者保留它。这种机制非常适合一些基本类型,比如NSInteger和CGFloat,或者就是不想直接拥有的类型,比如委托。assign相当于如下写法。

- (void)setTitle:(NSString *)newTitle {
title = newTitle;
}

retain参数会在赋值时把新值保留(发送retain消息)。此属性只能用于Objective-C对象类型,而不能用于基本数据类型或者Core Foundation。retain相当于如下写法:

(void)setTitle:(NSString *)newTitle {
[newTitle retain];
[title release];
title = [[NSString alloc] initWithString: newTitle];
[newTitle release]
}

copy在赋值时将新值拷贝一份,拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。copy相当于如下写法:

- (void)setTitle:(NSString *)newTitle {
[newTitle copy];
[title release];
title = [[NSString alloc] initWithString: newTitle];
[newTitle release]
}

Objective-C语言内存管理的更多相关文章

  1. Go语言内存管理(一)内存分配

    Go语言内存管理(一)内存分配 golang作为一种"高级语言",也提供了自己的内存管理机制.这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学 ...

  2. C语言 内存管理(转)

     转自 https://blog.csdn.net/u011616739/article/details/61621815 C语言 内存管理 1.内存分区 C源代码进过预处理.编译.汇编和链接4步生成 ...

  3. 深入理解Linux C语言内存管理

    问题不能拖,我这就来学习一下吧,争取一次搞定. 在任何程序设计环境及语言中,内存管理都十分重要. 内存管理的基本概念 分析C语言内存的分布先从Linux下可执行的C程序入手.现在有一个简单的C源程序h ...

  4. C语言内存管理(转)

    伟大的Bill Gates 曾经失言: 640K ought to be enough for everybody — Bill Gates 1981 程序员们经常编写内存管理程序,往往提心吊胆.如果 ...

  5. iOS学习08之C语言内存管理

    本次主要学习和理解C语言中的内存管理 1.存储区划分 按照地址从高到低的顺序:栈区,堆区,静态区,常量区,代码区 1> 栈区:局部变量的存储区域 局部变量基本都在函数.循环.分支中定义 栈区的内 ...

  6. 深入理解C语言内存管理

    之前在学Java的时候对于Java虚拟机中的内存分布有一定的了解,但是最近在看一些C,发现居然自己对于C语言的内存分配了解的太少. 问题不能拖,我这就来学习一下吧,争取一次搞定. 在任何程序设计环境及 ...

  7. 一个C语言内存管理模块的实现

    C 内存管理模块的编写 C语言手动管理内存很困难,有时候很难发现内存泄漏,这两天看了一下里面有写了一个简单的内存管理模块,发现挺精巧,可以有效检测内存泄漏 原理很简单,就是把C的malloc函数分配的 ...

  8. 黑马程序员——OC语言 内存管理

    Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) (一)计数器 每个对象内部都保存了一个与之相关联的整数,称为引用计数器,当 ...

  9. OC语言-05-OC语言-内存管理

    一.引用计数器 1> 栈和堆 栈 ① 主要存储局部变量 ② 内存自动回收 堆 ① 主要存储需要动态分配内存的变量 ② 需要手动回收内存,是OC内存管理的对象 2> 简介 作用 ① 表示对象 ...

随机推荐

  1. python 获取进程pid号

    #-*- encoding:UTF-8 -*- import os import sys import string import psutil import re def get_pid(name) ...

  2. Hadoop.2.x_WebUV示例

    一.网站基本指标(即针对于网站用户行为而产生的日志中进行统计分析) 1. PV:网页浏览量(Page View页面浏览次数,只要进入该网页就产生一条记录,不限IP,统计点每天(较多)/每周/每月/.. ...

  3. min-height

    1.min-height min-height:160px;height:auto!important;height:160px; min-height:160px; 设置对象box的最小高度,Fir ...

  4. event事件学习小节

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. SQL server 视图、范式

    视图 1.视图的概述       视图其实就是一条查询sql语句,用于显示一个或多个表或其他视图中的相关数据.视图将一个查询的结果作为一个表来使用,因此视图可以被看作是存储的查询 或一个虚拟表.视图来 ...

  6. C# 在 vs2010 上开发 ActiveX 控件 【千辛万苦啊~】

    由于B/S项目中需要使用到读卡器的功能,但是由于厂家只有提供一个 读卡的dll,那么怎样能在客户端使用读卡器呢,那么进过一番查找,最总决定使用ActiveX 控件来做.由于是第一次接触到 Active ...

  7. eval 与 Function

    var json='{"name":"lee","age":"15"}' function test () { var ...

  8. Oracle 常用数据类型(转)

    varchar2(6) 张三 --在jbk中是两个字节,在utm中是三个字节char(6) 张 三 --可以确定长度的用charclob --大存储,没事少用,当多余4000字节时,会用lob来存储, ...

  9. Java与C++面向对象不同点

    首先面向对象的语言有哪些?JAVA.C++.C#等等.但是呢很多人认为C#和C++有关系,其实一点关系都是没有滴.C#是仿Java做的,很多人都说是假Java,因为C#和Java太像了比如C#也有st ...

  10. loadrunner获取返回值为乱码

    找了很多方法,utf-8也设置了,还是不行,只有有转码方法了 web_reg_save_param("res2", "LB=\"msg\":\&quo ...