首先说明一下几块存储区域:

栈区(局部变量、函数参数值)

堆区(对象、手动申请/释放内存)

BSS区(未初始化的全局变量、未初始化的静态数据)

常量区(字符串常量以及初始化后的全局变量、初始化后的静态数据)

代码区(存放函数体的二进制代码)

1.为什么需要内存管理

由于移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当某个APP所占用的内存超过系统规定限定内存大小时,系统就会发出内存警告,系统会向该APP发送Memory Warning消息。收到此消息后,需要该APP回收一些不需要再继续使用的内存空间,如果该APP不理会这个消息,系统会强制退出这个程序(程序会崩溃)。

2.内存管理范围

所有的OC对象,对其他的数据类型无效,因为OC对象存放在堆区,堆区内存不会自动释放,而其他数据类型存放在栈区(实际上系统为在栈区的数据自动加上了autorelease消息),至于为什么对象是存放在堆区,而其他数据类型是存放在栈区,是因为其他数据类型存储空间是固定的,比如,int4个字节,double8个字节等等,而对象是在程序运行时动态分配存储空间的,不是固定的大小所以存放在堆区.

3.内存管理操作

每个对象内部都有8个字节存储计数器(unsigned long类型)

计数器显示的是当前对象的被引用的次数

计数器是判断对象内存是否需要回收的唯一依据,当计数器为0时,对象将被回收(存在一个例外,那就是对象值为nil时)

任何一个对象刚创建出来时,计数器为1

给对象发送消息,进行相应的计数器操作。

retain消息:使计数器+1,该方法返回对象本身

release消息:使计数器-1(并不代表释放对象)

retainCount消息:获得对象当前的引用计数器值 %ld %tu

当一个对象计数器为0的时候,对象回收之前会自动调用dealloc方法,类似于一个对象在回收之前的消息返回(或者说是"临终遗言"),一般会重写dealloc方法.

//Car.h文件

#import <Foundation/Foundation.h>
@interface Car : NSObject

 //定义了一个int类型的实例变量speed
@property(nonatomic,assign,readwrite) int speed; @end
//Car.m文件
#import "Car.h" @implementation Car //重写了dealloc方法
- (void)dealloc{
NSLog(@"速度为%d的车子挂了",_speed);
[super dealloc];
}
@end
//定义了一个Person对象
#import <Foundation/Foundation.h>
@class Car; @interface Person : NSObject //将Car类型的类作为实例变量
@property(nonatomic,retain,readwrite) Car *car; @end
 //Person.m文件
#import "Person.h"
#import "Car.h" @implementation Person
//重写了dealloc方法
- (void)dealloc{ [_car release];
NSLog(@"Person 挂了"); [super dealloc];
}
@end
//主函数
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Person.h" int main(int argc, const char * argv[]) {
@autoreleasepool { Person *p = [[Person alloc] init];
Car *bigBen = [[Car alloc] init];
Car *byd = [[Car alloc] init]; bigBen.speed = ;
byd.speed = ; //这里将bigBen给p这个对象时,使用@property生成的set方法中,对当前传进来的对象进行判断,是否与当前对象相同,不相同则对传进来的对象计数器做一次retain操作.
p.car = bigBen;
//同理 所以在p的dealloc方法中对当前_car做了一次release操作
p.car = byd; //打印计数器当前值
NSLog(@"当前p的计数器为:%lu",[p retainCount]); [p release]; [bigBen release];
[byd release]; }
return ;
}

上面的代码就综合使用到了retain,release,retainCount方法,还有dealloc方法的重写,这里特别说明的是dealloc方法重写一定要写[super dealloc],因为一个类继承父类(或者一个类关联另一个类),父类(另一个类)会有私有变量||私有方法等需要调用父类(另一个类)的dealloc方法来彻底回收私有变量||私有方法在该类中占用的那块内存.

4.内存管理注意事项

当一个对象计数器为0,自动调用了dealloc方法时,再对该对象进行retain操作是野指针操作(野指针:指向了僵尸对象的指针),开启了检测僵尸对象会报错(Edit Scheme中打开僵尸对象检测)

谁创建,谁release;谁retain,谁release;谁调用,谁release(有始有终,谁让计数器+1,就应该让计数器-1)

永远不要直接通过对象调用dealloc方法(实际上调用并不会出错)

一旦对象被回收了, 它占用的内存就不再可用, 坚持使用会导致程序崩溃(野指针错误)为了防止调用出错,可以将“野指针”指向nil(0)

5.@property参数

上面代码中的@property(nonatomic,retain,readwrite) Car *car;

实际上做了3件事

1)生成了Car * _car的实例变量

2)生成了Car * car的set、get方法声明

3)实现了Car *car的set、get方法

其中set方法实现的代码如下

- (void)setCar:(Car *)car{

if(_car != car){

[_car release];

_car = [car retain];

}

}

为什么生成的set方法会是这样,因为@property后面有参数修饰,表示我们传进来的是OC对象,需要做内存管理,MRC内存管理的条件下,@property的参数有以下几个

原子性 :

atomic 对属性加锁,多线程下线程安全,默认方式

noatomic 对属性不加锁,多线程下不安全,但是速度快

读写属性:  readwrite 生成set、get方法,默认方式

readonly 只生成get方法

set方法处理:assign 非OC对象,直接赋值,默认方式

retain 先release旧值,retain新值

copy  先release旧值,在copy新值

set、get方法命名:setter = set方法名:(这里的冒号不可以忘记) | getter = get方法名

注意事项:当两个对象相互引用时,因为按照常规写法相互retain,导致后面都不能释放,这里的解决办法是一个对象使用retain,一个使用assign.

MRC下的自动释放池的基本使用

当一个对象调用一次autorelease方法(一般是创建对象的时候调用),会将对象加载到栈顶的自动释放池(autoreleasepool),相当于在自动释放池的释放列表登记一次该对象,当自动释放池被销毁时,会对释放池所有登记列表上面的对象做一次release操作(不代表对象一定会在释放池销毁时一起销毁),对象调用autorelease方法,对象的计数器不变.

#import <Foundation/Foundation.h>

 @interface Car : NSObject

 @property(nonatomic,assign,readwrite) float speed;

 //这里返回值类型使用的是instancetype而不是id是因为instancetype作为返回值会检测返回值是否与接收参数类型一致,如果不一致编译器会给出警告
+ (instancetype)car; @end
#import "Car.h"

 @implementation Car

 + (instancetype)car{

 //这样调用类方法创建出的实例对象都会自动加载到栈顶的释放池
Car *c = [[[Car alloc]init]autorelease]; return c;
} - (void)dealloc{ NSLog(@"Car dealloc");
[super dealloc]; } @end
 #import <Foundation/Foundation.h>

 @class Car;

 @interface Person : NSObject

 @property(nonatomic,retain,readwrite) Car *car;

 + (instancetype)person;

 @end
 #import "Person.h"

 @implementation Person

 + (instancetype)person{

 Person *p = [[[Person alloc]init]autorelease];
return p;
} - (void)dealloc{ [_car release];
NSLog(@"Person dealloc");
[super dealloc]; } @end
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Person.h" int main(int argc, const char * argv[]) {
@autoreleasepool { Car *byd = [Car car];
Person *p = [Person person]; p.car = byd; }
return ;
}

对照两个案例的代码,可以看出自动释放池帮我们省去了对象创建时对应的release操作,我们也不用关心对象什么时候回收,需要注意的,占用内存较大的对象不要使用自动释放池回收,可能会导致占用内存很长时间,因为无法精确释放的时间,要注意的是,自动释放池会根据释放登记列表帮我们操作一次release,所以不要重复添加对象到自动释放池.另外相互引用解决办法与上面的MRC同理,一个使用retain,一个使用assign.

OC内存管理(MRC)的更多相关文章

  1. OC内存管理-OC笔记

    内存管理细节:http://blog.sina.com.cn/s/blog_814ecfa90102vus2.html 学习目标 1.[理解]内存管理 2.[掌握]第一个MRC程序 3.[掌握]内存管 ...

  2. Objective-c的内存管理MRC与ARC

    Objective-c的内存管理MRC与ARC   Objective-c中提供了两种内存管理机制MRC(MannulReference Counting)和ARC(Automatic Referen ...

  3. OC 内存管理机制总结

    OC 内存管理机制总结 一:OC内存管理机制目前分为两块,其一自动内存管理机制,其二手动内存管理机制: 1.首先我们从自动内存管理机制讲起: 1)什么是自动内存管理机制,自动内存管理机制就是程序中所创 ...

  4. OC内存管理基础

    OC 内存管理基础 一. retain和release基本使用 使用注意: 1.你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作) 2.你不想再使用(占用)某个对象,就 ...

  5. QF——OC内存管理详解

    堆的内存管理: 我们所说的内存管理,其实就是堆的内存管理.因为栈的内存会自动回收,堆的内存需要我们手动回收. 栈中一般存储的是基本数据类型变量和指向对象的指针(对象的引用),而真实的对象存储在堆中.因 ...

  6. OC内存管理-黄金法则

    1.内存管理-黄金法则 The basic rule to apply is everything that increases the reference counter with alloc, [ ...

  7. OC内存管理总结,清晰明了!

    <span style="font-size:18px;">OC内存管理 一.基本原理 (一)为什么要进行内存管理. 由于移动设备的内存极其有限.所以每一个APP所占的 ...

  8. 31 (OC)* 内存管理

    31 (OC)  内存管理 一:内存管理黄金法则. 如果对一个对象使用了alloc.[Mutable]copy,retain,那么你必须使用相应的realease或者autorelease 二:内存管 ...

  9. OC 内存管理之手动内存管理MRC

    一.基本原理 1.什么是内存管理 内存管理的重要性: 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间. ...

随机推荐

  1. Ubuntu系统启动过程详解

    作者:杨硕,华清远见嵌入式学院讲师. 一. Ubuntu的启动流程 ubuntu的启动流程和我们熟知的RedHat的启动方式有所区别. RedHat的启动过程如下图: 这是我们熟知的linux启动流程 ...

  2. MyEclipse使用SVN进行项目版本控制

    一.搭建SVN服务器. 例如,使用VisualSVN Server,下载后安装. (1)在Repositories(版本库)上右击,新建Repository,选择Regular FSFS reposi ...

  3. mysql语句分析

    explain的每个输出行提供一个表的相关信息,并且每个行包括下面的列: 1,id   select识别符.这是select的查询序列号.2,select_type 可以为一下任何一种类型simple ...

  4. js的基础学习

    alert() 函数在 JavaScript 中并不常用,但它对于代码测试非常方便. <!DOCTYPE html> <html> <body> <h1> ...

  5. varnish4.0简介

    Varnish 4.0 简介 Varnish 是一款开源的HTTP加速器和反向代理服务器,它的主要特点有: (1)是基于内存缓存,重启后数据将消失.(2)利用虚拟内存方式,io性能好.(3)支持设置0 ...

  6. MySQL Cluster(MySQL 集群) 初试(转)

    作/译者:叶金荣(imysql#imysql.com>),来源:http://imysql.com,欢迎转载. 作/译者:叶金荣(Email: ),来源:http://imysql.cn,转载请 ...

  7. SQL Server 2005导入Excel表问题

    EXCEL导入到SQL Server经常出现“文本被截断,或者一个或多个字符在目标代码页中没有匹配项” 原因: SQL Server的导入导出为了确定数据表的字段类型,取excel文件的前8行来判别. ...

  8. 【原创】angularjs1.3.0源码解析之scope

    Angular作用域 前言 之前我们探讨过Angular的执行流程,在一切准备工作就绪后(我是指所有directive和service都装载完毕),接下来其实就是编译dom(从指定的根节点开始遍历do ...

  9. HDOJ 2089 不要62

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  10. Y2K Accounting Bug(贪心)

    Y2K Accounting Bug Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 10945   Accepted: 54 ...