object-C 手动内存管理(MRC)
object-C的内存管理和javascript的垃圾回收不一样,今天总结下手动内存管理,ARC的后边补上。
1:基本铺垫
oc采用引用计数来表示对象的状态,比如通过init创建出来的一个对象引用计数为1,引用计数为0时对象被废弃。如果想让它释放则对这个对象发送一条release消息,则引用计数-1,那怎么+1呢,给这个对象发送retain消息。
对象操作 | Object-C方法 |
生成并持有对象 | alloc/new/copy/mutableCopy方法 |
持有对象 | retain方法 |
释放对象 | release方法 |
废弃对象 | dealloc方法 |
其中dealloc方法是对象内存被释放时执行的方法,可以对它进行重写。
(1)新建一个对象其引用计数为1;
Student * stu1 = [[Student alloc] init]; //
NSLog(@"%lu",[stu1 retainCount]);
(2)发送一条retain消息,引用计数+1;
[stu1 retain]; //+1 现在为:2
NSLog(@"%lu",[stu1 retainCount]);
(3)发送一条release消息,引用计数-1;
[stu1 release]; //-1 此时为1
NSLog(@"%lu",[stu1 retainCount]);
(4)引用计数为0时,对象的内存被释放。注意:自己主动通过retainCount方法,获取不到0那个状态(向引用计数为1的对象发送release消息),此时对象已销毁,内存回收,执行dealloc方法(已重写);
[stu1 release]; //-1,现在应该为0
NSLog(@"%lu",[stu1 retainCount]); //打印出来的是1
(5)已经成为0的引用计数,不允许再释放;
Student * stu1 = [[Student alloc] init]; //
[stu1 retain]; //
[stu1 release]; //
[stu1 release]; //
NSLog(@"%lu",[stu1 retainCount]); //
[stu1 release]; //崩溃
2:属性是自定义对象
先祭上内存管理三个法则:
一:new、alloc、copy均看成1(+1),此时意味着创建者拥有这个对象,创建者应该将其释放(-1);
二:谁+1,谁-1,要保证+1和-1的操作是平等的。(retain和release的次数保持平等)
如果一个对象a是另外一个对象b的实例变量,则成为b拥有a对象。接下来看下属性(或成员变量)是自定义对象的内存管理。
创建两个类Student , Book 。Student中有一个成员变量-Book对象。book中有一个方法read,read打印book对象,因是成员变量不是属性,所以重写了getter和setter方法,看一下book对象的内存管理。
文件结构:
Student.h文件:
#import "Book.h"
@interface Student : NSObject
{
Book * book;
}
- (void)setBook:(Book *)abook;
- (Book *)book;
- (void) read;
@end
Student.m文件:
@implementation Student
- (void)setBook:(Book *)abook
{
self->book = abook;
}
- (Book *)book
{
return book;
}
- (void)read
{
NSLog(@"我正在读书:%@",self->book);
}
@end
main函数:
Student * stu1 = [Student new];
Student * stu2 = [Student new];
Book * book1 = [Book new]; // [stu1 setBook:book1];
[stu2 setBook:book1]; [stu1 read];
[book1 release]; //
[stu2 read];
上面的写法中stu1 read之后就把book1释放了,这样stu2再去掉read方法的时候,book1已经没有了。可能有崩溃的情况发生(book1已经被释放,可是内存不一定被覆盖,如果被覆盖则会崩溃),这样写肯定不行。这样就不能防止意外释放的情况发生。那该怎么办呢?
在read方法中写book1 retain?这只是一个方法,如果有很多个方法呢?
在stu1或者stu2调用read方法的时候,最好能保证book1的引用计数最少为1,不用的时候再把多出来的+1减去。这样多出来的这个1。不管stu1或者stu2在掉read方法的时候book1的引用计数最少为1.那这个引用计数+1操作应该放到哪呢?放到设置值的setter方法中。先让book+1,这样的的话,stu1,stu2在setBook的时候book1引用计数都是+1.即使book1 release放到了stu1 read或stu2 read之前都没有关系,因为stu1和stu2每个对象上的book引用计数有额外的1:
@implementation Student
- (void)setBook:(Book *)abook
{
[abook retain];// +1
self->book = abook;
}
这样的话,book1的release放哪都没有问题了:
Student * stu1 = [Student new];
Student * stu2 = [Student new];
Book * book1 = [Book new]; //
[stu1 setBook:book1];
[stu2 setBook:book1]; [book1 release]; [stu1 read];
[stu2 read];
可是这样也产生了一个问题,即stu1和stu2每个对象上的book1的引用计数多了1。怎么办?按照内存管理原则:谁创建谁释放,setter中的book1并不是在main函数中+1,所以并不应该在main中执行release操作。那么这么说难道是在setter中执行release方法?像这样?
- (void)setBook:(Book *)abook
{
[abook retain];
self->book = abook;
[abook release];
}
显然这么做没有意义,那该怎么办呢?每个对象都有一个dealloc方法,在对象被释放的时候会执行。所以可以把book的release操作放在对象的dealloc方法中。这样当Student对象:stu1或stu2释放的时候,它自身的属性也就没有了存在的必要。这样stu1或stu2上book多的1可以减掉。此时释放很合适。OK,问题解决!=。=
Student.m文件中重写dealloc:
- (void)dealloc
{
//dealloc方法中,需要将那些通过setter曾经retain过的成员变量,在此要进行release。
[self->book release];
NSLog(@"atudent dealloc.....");
[super dealloc];
}
@end
NSLog(@"***************我是分割线*********************")
3:多对象问题
上面的问题似乎解决的很好,接下来看一下情况:
(1)第一种情况:只有一个学生对象,但是有两个书对象:
Student * stu1 = [Student new];
Book * book1 = [Book new]; //
[stu1 setBook:book1]; //
[book1 release]; //
[stu1 read];
//现在看第二本书
Book * book2 = [Book new]; //
[stu1 setBook:book2]; //
[book2 release]; //
[stu1 read];
[stu1 release]; //book2:0,book1:1
现在两个书本对象经过setter方法,引用计数都是+1,在stu1释放前引用计数都为1,可是stu1的dealloc方法中只是把当前它上边的属性release掉,而stu1当前的属性为book2。也就是说只有book2正常释放了,而book1没有释放。那该怎么办?
解决办法:改写setter
- (void)setBook:(Book *)abook
{
[self->book release];
self->book = [abook retain]; //这句=[abook retain]; self->book = abook;
}
这个时候让旧的属性release,然后让新的属性retain。以上边的为例:当book1负值的时候,book1成员变量为空,所以[self->book1 release]不执行。然后book1上赋值并且retain,引用计数+1,和之前一样,最后遗留引用计数1。
当book2走setter方法的时候,此时book1上有book1这个对象,那么第一条语句就会执行,那么book1的引用计数就会-1。下一条语句book1的成员变量的指针指向book2那块内存。完成赋值并且retain。引用计数+1.和先前一样。最后也可以正常释放。book1由于引用计数-1所以也能将遗留的1减去。最后也能正常释放。=。=问题解决
(2)第二种情况:还是那本书。(还是同样的对象,只是用了不同的指针去指向它)
Student * stu1 = [Student new];
Book * book1 = [Book new]; //1
[stu1 setBook:book1]; //2
[book1 release]; //1
[stu1 read];
//现在书坏了,修理下(用新的指针指向这本书)
Book * bookOld = book1; //还是1
[stu1 setBook:bookOld]; //为0了
[stu1 read];
[stu1 release];
那么现在的情况用之前setter的写法已然不合适,这样在设置bookOld的时候,先将老的release,再将新的retain。可是这里新的老的是同一个对象,将老的release掉之后,引用计数为0了,被释放。不合适。现在我们希望新的对象还是原来的那样写法,但是如果是老对象,我们不希望它的引用计数发生改变。那么现在需要做判断:
1:普通写法
- (void)setBook:(Book *)abook
{
if(self->book != abook){
[self->book release];
self->book = [abook retain];
}
}
2:文艺写法:
- (void)setBook:(Book *)abook
{
[abook retain];//先将新的retain //
[self->book release];//将旧的释放 //
self->book = abook;//将新的赋给成员变量,不需要再retain
}
这样的引用计数就可以正常释放了,如果是新对象则和第一种情况一样执行旧的release,新对象retain。如果还是老的对象,则还是同一块内存,则新的retain,而第二句老的release,而老的还是它,所以抵消。引用计数不发生改变,而这也是我们想要的。
O了。。。。。。。。。。。。。。。。。。。。。。。。
object-C 手动内存管理(MRC)的更多相关文章
- iOS-旧项目中手动内存管理(MRC)转ARC
在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事.我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题, ...
- OC 内存管理之手动内存管理MRC
一.基本原理 1.什么是内存管理 内存管理的重要性: 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间. ...
- Objective-c的内存管理MRC与ARC
Objective-c的内存管理MRC与ARC Objective-c中提供了两种内存管理机制MRC(MannulReference Counting)和ARC(Automatic Referen ...
- OC内存管理(MRC)
首先说明一下几块存储区域: 栈区(局部变量.函数参数值) 堆区(对象.手动申请/释放内存) BSS区(未初始化的全局变量.未初始化的静态数据) 常量区(字符串常量以及初始化后的全局变量.初始化后的静态 ...
- Objective-C 【内存管理&手动内存管理 综述】
------------------------------------------- 内存管理 (1)Objective-C的内存管理 栈区 存放局部变量(由于基本数据类型占用的存储空间是固定 ...
- IOS基础 Day-1手动内存管理
辞职回家打算自学IOS开发,就在借个地方记录一下 Day-1 手动内存管理 主要内容:release retain必须配对好,不然会占用内存 慢慢积累导 ...
- Xcode 如何设置 自动内存管理 转换为 手动内存管理
建议使用自动内存管理 ARC. 如果不想自动内存管理,可以在build phases 下的compile sources 中找到不想自动管理的.m文件 ,给它加compiler flags 为 -fn ...
- 【iOS开发-33】学习手动内存管理临时抛弃ARC以及retain/assign知识——iOSproject师面试必考内容
我们为什么须要内存管理?当使用内存达到40M和45M时候会发出警告,假设不处理,占用内存达到120M时直接强制关闭程序. 所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对 ...
- 内存管理-MRC与ARC详解
Objective-C提供了两种内存管理机制MRC(Mannul Reference Counting)和ARC(Automatic Reference Counting),为Objective-C提 ...
随机推荐
- 基础算法(二分,贪心):NOIP 2012 疫情控制
题目大意 给出一棵n个节点的树,根是1,要在除根节点以外的点建立检查点,使得从每条根到叶子的路径上都至少存在一个检查点.检查点由军队来建立.初始军队的位置是给定的,移动军队走一条边需要花费这条边的权值 ...
- Bzoj 1756: Vijos1083 小白逛公园 线段树
1756: Vijos1083 小白逛公园 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1021 Solved: 326[Submit][Statu ...
- (转载)TRS内容管理平台用户注册逻辑漏洞
首先 site:gov.cn inurl:WCM TRS 的内容管理系统是国内政府网站使用最多的系统之一 如上面所说:外交部 http://wcm.fmprc.gov.cn/wcm/ 网址加上:wcm ...
- kvm 性能调优
CPU Tuning Cache share tuning 对于物理 CPU,同一个 core 的 threads 共享 L2 Cache,同一个 socket 的 cores 共享 L3 cache ...
- Django中的ORM进阶操作
Django中的ORM进阶操作 Django中是通过ORM来操作数据库的,通过ORM可以很easy的实现与数据库的交互.但是仍然有几种操作是非常绕也特别容易混淆的.于是,针对这一块,来一个分类总结吧. ...
- 【转】谁说Vim不是IDE?(二)
谁说Vim不是IDE?(二) 环境配置 “如果你认为Vim只是一个文本编辑器,你就输了”——来自Vim老鸟 Vim以简洁的方式提供了丰富的配置功能,主要配置体系由一个文件和文件夹组成.在一台安装了 ...
- 【oracle】初学jobs
含义:job是oracle的一种对象,可以理解为定时执行的程序 目的:定时自动执行特定代码 照猫画虎--创建job 1.创建测试表JOB_TEST create table JOB_TEST(a da ...
- Asp.net mvc 自定义全局的错误事件HandleErrorAttribute无效
Asp.net mvc 自定义全局的错误事件HandleErrorAttribute,结果无效, 原因: 1.没有在RegisterGlobalFilters 里面添加或者你要的位置添加. 2.你把这 ...
- ASP.NET MVC- MvcPager
本文目标 一.能够使用MvcPager进行分页 本文目录 一.MvcPager控件的简单使用 二.C#扩展方法 一.MvcPager控件的简单使用 1.添加MvcPager.dll的引用[下载] 2. ...
- 圣诞节来了,雪花纷飞的CSS3动画,还不首页用起来
圣诞节来了,冬天来了,怎么可以没有雪花纷飞效果,昨天下班前折腾了一会儿,弄了个雪花纷飞的实例,有兴趣的可以交流分享下. 原文链接:http://www.html5think.com/article/i ...