OC中的内存管理

来源:互联网 发布:六轴机械手臂 算法 编辑:程序博客网 时间:2024/05/16 15:22

1.内存管理的范围


程序在系统中分配内存有五个地方:BSS段 数据区 代码区 (这三个是在程序运行时加载到内存空间的)

栈区(存储的是局部变量等系统自动回收的数据,高地址->低地址) 堆区(存储动态分配的数据,低地址->高地址

所以在OC中的内存管理,就是管理堆区中的对象。也就是继承自NSObject的对象。


2.原理以及分类


对象的所有权以及引用计数:

一个对象可能有不止一个所有者。只要还有一个所有者,就继续存在。

retain方法可以获得对象的所有权,每个对象在创建之初就有一个retainCount引用计数器,它是unsigned long类型,在内存中占8个字节。

引用计数器是判断一个对象是否被回收的重要依据,当使用new,copy或者alloc创建一个对象时,默认为1。当retainCount为0时,回收对象。但是有一种特殊情况,当对象被赋值为nil类型时,不回收。为啥?nil是空值,没分配内存,回收个毛线。


retain消息 :加1操作,返回对象的本身;

release消息: 减1操作;

retainCount消息:计数器的值,用%ld,%tu,%lu 表示


dealloc消息:当一个对象被回收时,发送dealloc消息。

一般重写dealloc方法,会用到[super dealloc]只能放在最后调用。


内存管理只要有三类:MRC(手动内存管理)ARC(自动内存管理,底层就是MRC)垃圾回收机制(苹果不支持)

主要使用ARC,方便╮(╯_╰)╭,但是要理解MRC,本质( ⊙ o ⊙ )


3.MRC(手动内存管理)简介


Xcode默认是ARC机制的,需要手动更改,在Build Setting中,搜索auto,找到ARC。

Person *p = [Person new]; //retainCount ==1

[p retain]; // 2

[p release]; //1

[p release]; // 0


-(void) dealloc{

NSLog(@"已进入下一次轮回序列... ... ");

[super dealloc];// 子类释放后,父类也得来一下,不然显得不那么情深意浓

}


管理原则:找到平衡很重要,用就加1,不用就减1.有人使用就不回收。

  谁创建 谁release;谁retain,谁release;

管理内容

野指针:定义的指针变量没有初始化;指向的空间已经被释放;

内存泄露:内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。即所谓内存泄漏。


单个对象的内存管理:

是否检测僵尸对象,需要手动更改。如果不检测,野指针当中有无意义的数据。不能用retain使僵尸对象复生。

给空指针发送消息是没有反应的。避免使用僵尸对象的方法是:给僵尸对象赋值为nil。

多个对象的内存管理:

一个对象在销毁之前可以任意调用方法,但是这个对象可能关联的其他对象已经被销毁。注意释放顺序。


set方法的内存管理:

如果对象作为另一个类的实例变量。先判断传递对象是否为新对象,如果是,对旧对象release,对新对象retain

if(_car != car) {

[_car release];

 _car =  [car retain];}

如果不是 ,什么也不做就可以。


@property参数 (1,2)

原子性:  atomic  对属性加锁,多线程下线程安全,默认值

nonatomic  对属性不加锁,不安全但是速度快

读写属性:readwrite 生成getter setter方法,默认值

 readonly 只生成getter方法

set方法处理:assign 直接赋值,默认值           //实例变量是基本数据类型

retain 先release原值,再retain新值

copy   先release原值,再copy新值

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

注意重写dealloc方法;


替换set和get方法名称:

@property (nonatomic,assign )BOOL vip;

@property(nonatomic,assign setter = isVip:,getter= isVIP)BOOL vip;//注意set方法的冒号:


@class的使用:@class 类名xx;

#import 是拷贝内容,当发生变化的时候需要重新拷贝。

@class 告诉编译器是xx一个类,有哪些属性方法,不去检测,好像在说,哥们相信我!

但是编译器不是个傻子,它只是相信是个类,至于里面的属性方法,用的时候会出问题,所以需要#import xx.h

当涉及到循环引用的时候,#import 是拷贝内容就会出错,而@class不会。


循环retain的问题

引用头文件时需要使用@class

循环retain会导致两个对象都存在内存泄露问题。

推荐方法:

1)让某个对象多释放一次,但是需要注意顺序;

2)一端使用retain,一端使用assign;


NSString类的内存管理:

字符串的常量 会存在字符串的常量池当中,如果使用的字符串已经存在,不会重新分配内存。

常量区的引用计数是无符号的最大值,如果release 没有结果

initWithFormat 、stringWithFormat 分配在堆区 //同样不要release 因为没alloc 

stringWithString、initWithString、@“ad”、alloc init分配都是在常量区

轻易不要使用retainCount作为判断条件,有可能循环好久好久... ...


autorelease的使用

自动释放池,@autoreleasepool {}(5.0以后),它是一个特殊的栈结构(先进后出),结束时会向池中的对象自动发送release消息。

  1. 创建自动释放池
  2. 对象加入自动释放池 [对象名 autorelease];加入不会改变计数器的值
再调用autorelease的时候,如果不在autoreleasepool中,也不会加入。
自动释放池的嵌套:
自动释放池的栈结构和内存中栈区的结构不一样。不适宜放内存比较大的对象。
尽量不要把autorelease当release用。不适宜把大量循环放在自动释放池。

autorelease的应用场景:

创建一个类方法,使其自动创建对象并且添加到自动释放池。

+(id) person{

return [[Person alloc] init  autorelease]; //返回对象的空间

}

系统自带很多类具有这种方法:

NSArrary *arr = 【NSArray array】;

NSDictionary *dic = 【NSDictionary dictionary】;


动态类型: 

Student *stu = 【Student  person】;//子类使用父类的方法

+(id) person{

return [[self  alloc] init  autorelease]; //返回对象的空间

}


存在的问题:

当赋值两遍的类型不一致的时候,使用返回值id在编译的时候是不会报错的。

id和instancetype的区别:

instancetype可以判断返回值类型和赋值的指针类型(接收类型)是否一致。id不能智能检测。

当一个方法的返回值是对象的时候,最好使用instancetype类型。

+(instancetype)person{

return [[Person alloc] init autorelease];

}




0 0
原创粉丝点击