object-C 手动内存管理(MRC)

object-C的内存管理和javascript的垃圾回收不一样,今天总结下手动内存管理,ARC的后边补上。

1:基本铺垫

oc采用引用计数来表示对象的状态,比如通过init创建出来的一个对象引用计数为1,如果想让它释放则对这个对象发送一条release消息,则引用计数-1,那怎么+1呢,给这个对象发送retain消息。


对象操作

Object-C方法
生成并持有对象 alloc/new/copy/mutableCopy方法
持有对象 retain方法
释放对象 release方法
废弃对象 dealloc方法

其中dealloc方法是对象内存被释放时执行的方法,可以对它进行重写。

栗子:

(1)新建一个对象其引用计数为1;

Student * stu1 = [[Student alloc] init];   //1
NSLog(@"%lu",[stu1 retainCount]);          //1

(2)发送一条retain消息,引用计数+1;

[stu1 retain];                     //2
NSLog(@"%lu",[stu1 retainCount]);  //2

(3)发送一条release消息,引用计数-1;

[stu1 release];                    //1
NSLog(@"%lu",[stu1 retainCount]);  //1

(4)引用计数为0时,对象的内存被释放。注意:自己主动通过retainCount方法,获取不到0那个状态(向引用计数为1的对象发送release消息),此时对象已销毁,内存回收,执行dealloc方法(已重写);

[stu1 release];  //应该为0
NSLog(@"%lu",[stu1 retainCount]);  //打印出来的是1

(5) 已经成为0的引用计数,不允许再释放;

Student * stu1 = [[Student alloc] init];   //1

[stu1 retain];                           //2
[stu1 release];                          //1

[stu1 release];                         //0
NSLog(@"%lu",[stu1 retainCount]);       //1

[stu1 release];   //崩溃
NSLog(@"%lu",[stu1 retainCount]);

2:自定义对象

先祭上内存管理三个法则:

  一:new、alloc、copy均看成1(+1),此时意味着创建者拥有这个对象,创建者应该将其释放(-1);

  二:谁+1,谁-1,要保证+1和-1的操作是平等的。(retain和release的次数保持平等)

以下是大致思路:

(1)创建两个类Student , Book 。Student中有一个成员变量-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];  //1
Book * book1 = [Book new];       //1
[stu1 setBook:book1];

[stu1 read];

[book1 release];                //0
[stu1 release];                 //0

上面的写法复合内存管理:谁创建,谁释放(在main中创建,在main中释放)、谁+1,谁-1。可是这么做很依赖书写顺序,比如book1 release方法在read方法之前,那么对象已经被释放就不能调read方法。这样就不能防止意外释放的情况发生。那怎么办呢?

咱们可以在read方法中对book1对象发送retain消息,防止book1对象被释放。

- (void)read
{
    [self->book retain];
    NSLog(@"我正在读书:%@",self->book1);
}

这样做可行,可是如果我有很多个方法呢?难道每个方法都要写一遍book release?

这时可以把book release方法写到Student初始化方法setter中。进行+1操作.(问题:可以写到student的init方法中吗?)

- (void)setBook:(Book *)abook
{
    [abook retain];
    self->book1 = abook;
}

这样就可以解决book1对象被意外释放的问题,即使book1的release方法在read之前也没有关系,有多个属性也没关系,我都可以写到Student的setter方法中。

Student * stu1 = [Student new];
Book * book1 = [Book new];         //1
[stu1 setBook:book1];              //2

[book1 release];                  //1
[stu1 read];                      //1

[stu1 release];

可是这样也产生了一个问题,即book1的引用计数多了1。怎么办?按照内存管理原则:谁创建谁释放,setter中的book1并不是在main函数中+1,所以并不应该在main中执行release操作。那么这么说难道是在setter中执行release方法?像这样?

- (void)setBook:(Book *)abook
{
    [abook retain];
    self->book = abook;
    [abook release];
}

显然这么做没有意义,那该怎么办呢?

解决办法是book1的release方法在student的重写的dealloc方法中执行。(这样的设计很巧妙有木有?)当student释放的时候,它自身的属性也就没有了存在的必要。OK,问题解决!=。=

- (void)dealloc
{
    [self->book release];     //dealloc方法中,需要将那些通过setter曾经retain过的成员变量,在此要进行release.
    NSLog(@"student dealloc.....");
    [super dealloc];
}

NSLog(@"***********************************我是分割线****************************************************")

3:多对象问题

上面的问题似乎解决的很好,接下来看一下情况:

(1)第一种情况:现在还是那个学生对象,但是有两个书对象:

Student * stu1 = [Student new];
Book * book1 = [Book new];   //1
[stu1 setBook:book1];        //2
[book1 release];             //1
[stu1 read];
//现在看第二本书
Book * book2 = [Book new];    //1
[stu1 setBook:book2];         //2
[book2 release];              //1
[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];
}

这个时候让旧的属性release,然后让新的属性retain。以上边的为例:当book1负值的时候,book1成员变量为空,所以[self->book1 release]不执行。然后book1上赋值并且retain,引用计数+1,和之前一样,最后遗留引用计数1。

当book2走setter方法的时候,此时book1上有book1这个对象,那么第一条语句就会执行,那么book1的引用计数就会-1。下一条语句book1的成员变量的指针指向book2那块内存。完成赋值并且retain。引用计数+1.和先前一样。最后也可以正常释放。book1由于引用计数-1所以也能将遗留的1减去。最后也能正常释放。  OK  -解决=。=

(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的写法已然不合适,:还是原来的那个对象执行下面语句则

[self->book release];                         执行到这里的时候,book1就为0了,不合适。

self->book = [abook retain];

现在如果还是老对象则希望它的引用计数不发生改变

那么现在需要做判断:

1:普通写法:

- (void)setBook:(Book *)abook
{
    if(self->book != abook){     //如果是新对象执行下面操作,如果不是新对象,则什么也不执行
        [self->book release];
        self->book = [abook retain];
    }
}

2:文艺写法:

- (void)setBook:(Book *)abook
{
    [abook retain];//先将新的retain //2
    [self->book release];//将旧的释放 //1
    self->book = abook;//将新的赋给成员变量,不需要再retain
}

这样的引用计数就可以正常释放了,如果是新对象则和第一种情况一样执行旧的release,新对象retain。如果还是老的对象,则还是同一块内存,则新的retain,而第二句老的release,而老的还是它,所以抵消。引用计数不发生改变,而这也是我们想要的。

O了。。。。。。。。。。。。。。。。。

时间: 2024-10-07 02:32:21

object-C 手动内存管理(MRC)的相关文章

iOS-旧项目中手动内存管理(MRC)转ARC

在ARC之前,iOS内存管理无论对资深级还是菜鸟级开发者来说都是一件很头疼的事.我参 加过几个使用手动内存管理的项目,印象最深刻的是一个地图类应用,由于应用本身就非常耗内存,当时为了解决内存泄露问题,每周都安排有人值班用 Instruments挨个跑功能,关键是每次都总能检查出来不少.其实不管是菜鸟级还是资深级开发者都避免不了写出内存泄露的代码,规则大家都懂,可是 天知道什么时候手一抖就少写了个release? 好在项目决定转成ARC了,下面将自己转换的过程和中间遇到的问题写出来和大家共享,希望

OC 内存管理之手动内存管理MRC(MannulReference Counting)

一.    基本原理 1.        什么是内存管理 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间.比如回收一些不需要使用的对象.变量等 管理范围:任何继承了NSObject的对象,对其他基本数据类型(int.char.float.double.struct.enum等)无效 2.        对象的基本结构 每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人

iOS开发之oc(十一)--内存管理MRC

掌握内容 >理解内存管理的原理 >掌握手动内存管理MRC >掌握内存管理在实际工程中的使用 (一) 1.理解部分 1.1内存管理 (传统内存管理demo) 1.1.1内存管理做了一件什么事? 内存管理就是确保开辟的堆空间得到正确的释放 如果堆空间没有释放,称为内存泄露 使用已释放的堆空间,称为提前释放 重复释放同一个空间,称为重复释放 1.1.2传统内存管理的困境 (1)当我们要释放一个堆,首先要确定使用这个堆的指针,都访问完毕,避免提前释放. (2)释放指针指向的堆空间,首先要确定那些

Objective-c的内存管理MRC与ARC

Objective-c的内存管理MRC与ARC Objective-c中提供了两种内存管理机制MRC(MannulReference Counting)和ARC(Automatic Reference Counting),分别提供对内存的手动和自动管理,来满足不同的需求.注意的是Xcode 4.1及其以前版本没有ARC,MRC与ARC的区别如图1所示.需要理解MRC,但实际使用时强推ARC. 图1  MRC与ARC区别示意图   1. Objective-c语言中的MRC(MannulRefer

OC 内存管理:MRC与ARC

内存中的五大区域: 栈区,堆区,BBS段,数据段和代码段,其中除了堆区以外,其他区域的内存管理由系统自行回收 OC对象是存储在堆区的,所以OC的内存管理主要是对”堆区中的OC对象”进行管理 内存管理中的几个概念: ->引用计算器:既retainCount,每个OC对象内部都有1个8字节空间用来存储retainCount,表示有多少”人”正在使用; 对象刚被创建时,默认计数值就为1,当计数值为0时,系统会自动调用dealloc方法将对象销毁 引用计数器的用法:给对象发送相应的技术操作来改变计数器的

黑马程序员---OC基础6【内存管理】【手动内存管理】【单、多个对象的内存管理】【*@property参数】【@class的使用】【NSString类的内存管理】【autorelease使用】

------- iOS培训.Android培训.Java培训.期待与您交流! ---------- [内存管理] 1.内存管理概念 由于移动设备内存及其有限,所以每个app所占的内存也是有限的 需要回收一些不使用的空间 2.OC内存管理的范围 管理任何继承NSOject的对象,对其他的基本数据类型无效 主要管理堆区中的对象的内存管理   3.内存管理的原理 1)对象所有权概念 任何对象都可以能拥有一个或多个所有者,只要一个对象至少还拥有一个所有者,他就会继续存在 cocoasu所有权策略 任何自

Objective-C 【内存管理&手动内存管理 综述】

------------------------------------------- 内存管理 (1)Objective-C的内存管理 栈区    存放局部变量(由于基本数据类型占用的存储空间是固定的,由系统去分配,我们不用去管,故栈区存放基本数据类型,) 堆区    存放程序运行过程中动态分配的内存空间(对象类型是程序运行过程中动态分配的,他们的大小不固定.比如说是我们Person new申请来的,存放在堆区,也是我们需要管理的) ★所以内存管理的范围是   继承了NSObject的所有对象

手动内存管理和自动释放池

手动内存管理 在进行内存管理的时候要注意内存管理的准则:谁开辟内存,谁释放内存(谁污染的谁治理) .开辟内存之后,对象的引用计数为1,只有继承自NSObject的对象才有内促管理的概念, 当对象引用计数为0的时候对象的内存会被清理. 下列关键字会开辟内存,对象引用计数器+1 alloc new copy mutableCopy 下列是内存管理的相关方法. retain :保留对象,对象的引用计数器+1. release : 释放对象,对象引用计数器-1. retainCount : 获取对象当前

【iOS开发-33】学习手动内存管理临时抛弃ARC以及retain/assign知识——iOSproject师面试必考内容

我们为什么须要内存管理?当使用内存达到40M和45M时候会发出警告,假设不处理,占用内存达到120M时直接强制关闭程序. 所以出现闪退除了是程序出现逻辑错误,还有可能是内存使用过大. (1)创建一个对象的过程:先分配内存空间存储对象:初始化成员变量:返回对象的指针. (2)对象在创建时,内部会自己主动创建一个引用计数器retainCount,当retainCount=0时,系统会回收当前对象,retainCount是唯一推断标记.release会-1.retain会+1,retain后返回的是自