OC-内存管理


1.什么是内存?



程序运行中临时分配的存储空间,在程序结束后释放;


2.为什么我们要进行内存管理?



很多同学,玩过手机游戏吧,有没有发现你经常玩个4,5或者更长时间的游戏,要么手机变的很卡,要么直接闪退,这里他就体现了内存管理。

一般我们手机的内存是有限的,程序的内存会随着你程序的不断运行,对象不断增加,内存也会成成成的往上涨,一直到系统资源被耗尽。

mac OS和iOS在内存管理上有很大的区别,Mac OS 有垃圾回收机制,但iOS没有,iOS内存远不足于Mac OS系统,iOS将内存管理的任务交给了开发者。


3.内存管理的原理



保证每个对象在使用的时候存在于内存中,不用的对象在最后从内存中清除。

一个对象的生命周期:
1.对象的初始化
2.执行,使用
3.释放

对象使用内存结束后,需要释放自身所占的资源,归还给系统,以保证别的对象可以使用。

注意:内存管理只针对继承NSObject的对象,对其他的基本数据类型(int,float,double...)无效。

本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。


4.retainCount



Cocoa采用引用计数的技术来确定对象的生命周期结束
我们通过retainCount来知道对象是否以利用完改内存


在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。


1.每个OC对象都有自己的引用计数器,retainCount
2.当对象被创建时,引用计数置为1(alloc、copy或则new),注意是置为1,不是加1
3.使对象的引用计数+1,并且获得对象的所有权;
4.使对象的引用计数-1,并且放弃对象的所有权;
5.当引用计数值为0的时候,对象将被系统销毁。


那OC中,使用什么来控制对象的引用计数呢?



retainCount:获取对象当前的引用计数值;
alloc:返回一个对象,并将其引用计数设置为1,类方法;
retain:将对象的引用计数加1;
release:将对象的引用计数减1;
autorelease:将对象加入自动释放池,对象引用计数滞后减1;
除了上述方法之外,还有对象销毁的方法dealloc,我们不会主动调用,但是需要在类中重写;


接下来我们在非ARC模式下,进行内存管理


    普通情况下的内存管理


Person * person = [[Person alloc] init];
//1
[person retain];
//2
NSLog(@"%ld", [person retainCount]);
[person retain];
//3
NSLog(@"%ld", [person retainCount]);
[person release];
//2
NSLog(@"%ld", [person retainCount]);
[person release];
//1
NSLog(@"%ld", [person retainCount]);
[person release];

从我们推断,会发现到最后person对象的retainCount因为为0,为什么他的显示结果依然为1呢?

由于悬垂指针的原因,最后为1,内存已经回收,但是你的指针还存在,感觉还在指向一个内存空间。retainCount仅仅作为参考,不能进行逻辑计算

其实它已经为0,系统回收了这块内存,对象进行销毁。
对象销毁::dealloc,释放对象内部的成员变量,该方法不要手动调用,当对象retainCount为0的时候自动调用

*再次注意:OC中内存管理规则仅仅使用与对象类型,简单,复杂数据类型无需管理内存。可以点击取看,所有的relesea方法,都是基于NSObject



2.内存管理原则



常见的内存区域:

堆:需要的时候分配内存,不需要的时候手动释放,编译器不负责释放改区域的内存空间
栈:需要的时候分配内存,不需要的时候自动释放,编译器负责释放改区域的内存空间
全局/静态存储区:存储全局变量和静态变量
常量存储区:存储常量,不可变的数据
自由存储区

/*1.使用alloc,new,copy,mutableCopy生成的对象 对象初始引用计数值为1,手动释放内存(堆上的对象)
2.便利初始化生成的对象,对象初始引用计数值为1,并且设置为自动释放,无需手动释放(栈上的对象)
3.使用retain持有的对象,需要保证retain和release次数相等*/


3.setter,getter方法的内存管理(重点,难点)



main.m

Book * book = [[Book alloc] init];
Person * person = [[Person alloc] init];

[person setBook: book];
NSLog(@"%@", person);

[book release];
[person release];

person.m
- (void)setName:(NSString *)name
{
//释放旧的,持有新的对象
//为了避免相同对象赋值之后造成的对象意外释放,需要判断对象是否相同方法1
if (_name != name) {
[_name release];
_name = [name retain];//保证新的存在
}
//方法2
// [name retain];
// [_name release];
// _name = name;

//如果付的值相同,直接走get方法
}


- (void)setBook:(Book *)book
{
if (_book != book) {
[_book release];//放弃旧的所有权
_book = [book retain];
}
}

- (Book *)book
{
return _book;
}


4.自定义初始化



main.m

NSArray *score = [[NSArray alloc] initWithObjects:@"1", nil];
NSLog(@"score = %ld",score.retainCount);//1

Person *person = [[Person alloc] initWithName:@"lili" score:score age:12];
NSLog(@"score = %ld",score.retainCount);//2,初始化的时候,持有了score
[score release];
NSLog(@"score = %ld",score.retainCount);

person.m
//下划线只是一个成员变量,指针会指向一个内存地址,每次换,每次都会指向一个新的内存地址,不断的换名字,我们要进行管理
- (instancetype)initWithName:(NSString *)name
score:(NSArray *)score
age:(NSInteger)age{

self = [super init];
if (self) {
//对应的release在dealloc方法里面
_score = [score retain];
_name = [name retain];
_age = age;

}
return self;
}


5.集合内存管理:字典,数组



1.对象加入集合对象,+1
2.对象从集合对象中移除,-1
3.集合对象的retaincount改变,其中元素的retaincount不便
4.集合对象销毁时,其中所有的元素release一次

Person *person = [[Person alloc] init];
NSLog(@"person = %ld",[person retainCount]);//1

NSArray *array = [[NSArray alloc] initWithObjects:person, nil];
NSLog(@"person = %ld,array = %ld",
[person retainCount],//2
[array retainCount]);//1
[array retain];
NSLog(@"person = %ld,array = %ld",
[person retainCount],//2
[array retainCount]);//2
[array release];
[array release];
NSLog(@"person = %ld,array = %ld",
[person retainCount],//1因为数组没有了,所有的元素都会relea一次
[array retainCount]);//0(1)

//字典也使一样的


    自动释放池 NSAutoreleasePool


//NSAutoreleasePool:自动释放池类或对象
//类似于一个容器,所有加入容器中的对象都被他管理,在自动释放池,自身销毁的时候,池将会释放(release)所有的对象
//autorelease 方法把对象加入池中,一个对象可以被多次autorelease
//便利初始化的对象默认已经加入当前池中

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//pool 1
Person *person = [[Person alloc] init];//1
[person retain];//2
NSLog(@"person = %ld",[person retainCount]);
[person retain];//3
NSLog(@"person = %ld",[person retainCount]);
[person autorelease];//3
NSLog(@"person = %ld",[person retainCount]);
[person autorelease];//3
NSLog(@"person = %ld",[person retainCount]);
[person autorelease];//3
NSLog(@"person = %ld",[person retainCount]);
[pool release]; //person = 0(1)
NSLog(@"person = %ld",[person retainCount]);


7.便利初始化方法



NSArray *score = [[[NSArray alloc] initWithObjects:@"1", nil] autorelease];
[person setScore:score];//2
[pool release];//1
[score release];
//可以用来代替release,最后在pool release -1


上面这个和下面的等价
NSArray *score1 = [NSArray arrayWithObjects:@"1", nil];


8.自定义便利初始化方法



main.m

Person *person1 = [Person PersonWithName:@"kiki" score:score age:11];

person.m

+ (Person *)PersonWithName:(NSString *)name
score:(NSArray *)score
age:(NSInteger)age{

Person *person = [[Person alloc] initWithName:name score:score age:age];
return [person autorelease];
}


9.快速生成



NSArray *array3 = @[@"1",@"2",@"3"];
//数组,字典的快速生成:便利初始化,不可变数据


10 异常情况



//1、混乱释放
Person * person = [[Person alloc] init];
Person * person1 = person;
[person1 release];//不遵守内存管理原则
[person release] 较标准

//2、内存泄露
Person * person1 = [[Person alloc] init];
Person * person2 = [[Person alloc] init];
person2 = person1;//指针指向发生改变,person2原有内存 泄露, 解决方案 autorelease

//3、过度释放
Person * person = [Person personWithName:@"tom" age:12];
[person release];//便利构造器初始化的对象,过度释放了,不需要release

//4、nil对象的引用计数为0
Person * person = nil;
NSLog(@"%lu", [person retainCount]);

//5、常量对象的引用计数为无穷
NSString * name = @"name";
NSLog(@"%lu", [name retainCount]);
//name对象放在常量区

//6.针对NSString对象
NSString *string = [[NSString alloc] initWithFormat:@"213"];
NSLog(@"string = %ld",[string retainCount]);
//在堆中分配内存,都用引用计数模式,该string对象是常量区,用%d打印是-1,%lu就是正无穷
NSMutableString *mutableString = [[NSMutableString alloc] initWithFormat:@"111"];
NSLog(@"mutalbe = %ld",[mutableString retainCount]);
//nsmutableString创建的对象,是在堆中分配内存的,遵循引用计数模式

相关文档

  • 毕业生意见
  • java三大特性的优点
  • Apache Kylin 添加登录用户和密码(修改登录名和密码)
  • 旷古无两的成语解释
  • 外贸业务助理英文简历范文
  • 外地人和北京人结婚怎么领结婚证
  • 周公解梦之梦到水里死人
  • 新安怡玻璃奶瓶贝亲玻璃ppus奶瓶对比新安怡奶嘴好还是贝亲奶嘴好
  • 高姿和韩后哪个好?高姿和韩后哪个档次高?
  • 冷烫烫发水的使用方法冷烫烫发水怎么用
  • 议案的写作格式与写法
  • 小学四年级上册生命与健康常识教案
  • 去日本值得买的护肤品
  • java new一个对象的过程
  • 母亲河的作文
  • SpringClound??Config??尚硅谷阳哥学习视频笔记
  • 给杂志社的感谢信
  • 2017新年感恩短信
  • 清明节为什么要踏青?清明节踏青有什么好处?
  • 怎么冻成透明冰块
  • 习惯一个人坚强说的说
  • 删除
  • 以尊严为话题的作文叙事
  • 2017最新中国十大保险公司排行名单一览
  • 澳洲留学悉尼大学申请条件及费用
  • 养鸡什么时候能养一年能养几次
  • 5分钟大学演讲稿范文最新集合
  • 马斯克:SpaceX资金能满足星链计划第一主要阶段需要
  • 茉莉花花期多长时间 花期是什么时候
  • 天子蓝七彩神仙鱼好养吗,怎么养
  • 电脑版