OC+1-内存管理
来源:互联网 发布:青花瓷 知乎 编辑:程序博客网 时间:2024/05/31 19:47
内存管理的基本概念及范围
内存管理:系统会向app发送memory waring消息,收到消息后,需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等,否则程序会崩溃。
管理范围:管理任何继承NSObject的对象,对其他的基本数据类型无效(对象和其他数据类型在内存的存储位置不一样)
内存管理主要是对堆区中对象的内存管理
内存管理的原理及分类
原理:
对象的所有权及引用计数:任何对象都可能拥有一个或多个所有者,只要一个对象至少还拥有一个所有者,它就会继续存在
引用计数器的作用:在每个OC对象的内部,都有专门8个字节的存储空间来存储引用计数器,判断对象要不要回收(存在一种例外,对象值为nil,引用计数为0,但不回收空间)
对引用计数器的操作:1、retain消息:是计数器+1,该方法返回对象本身
2、release消息:是计数器-1(并不代表会释放)
3、retainCount消息:获得对象当前的引用计数器值
对象的销毁:当一个对象的引用计数器为0时,name它将被销毁,其占用的内存被系统回收。当对象被销毁时,系统会自动向对象发送dealloc消息,一般会重写dealloc方法。一旦重写dealloc方法,必须调用[super dealloc],并且放在代码块的最后调用(不能直接调用dealloc方法)。
一旦对象被回收了,那么该空间就不可再用,坚持使用会导致程序崩溃(野指针错误)。
当使用alloc、new和copy出的对象,默认引用计数器为1
内存管理分类:
OC提供了三种内存管理方式:1、Mannul Reference Counting(MRC,手动管理,在开发iOS4.1之前的版本的项目时,我们要自己负责使用引用计数来管理内存,比如要手动retain、release、autorelease等,而在其后的版本可以使用ARC,让系统自己管理内存)
2、automatic reference counting (ARC,自动引用计数)
3、garbage collection(垃圾回收,iOS不支持垃圾回收)
开发中如何使用:需要理解MRC,但实际使用时尽量用ARC
手动内存管理快速入门
//用person实例一个对象
Person *p = [Person new];
//证明p有一个所有者
NSUInteger count = [p retainCount];//用一个8字节无符号long类型来存储
NSLog(@"count = %lu", count);
//是引用计数器+1
//让p的所有者增加1,让p1去指向
Person *p1 = [p retain];
NSLog(@"p1.retainCount = %lu", [p1retainCount]);
//回收对象,使retainCount == 0
[p release];
NSLog(@"p.retainCount = %lu", [pretainCount]);
Person *p = [Person new];
//证明p有一个所有者
NSUInteger count = [p retainCount];//用一个8字节无符号long类型来存储
NSLog(@"count = %lu", count);
//是引用计数器+1
//让p的所有者增加1,让p1去指向
Person *p1 = [p retain];
NSLog(@"p1.retainCount = %lu", [p1retainCount]);
//回收对象,使retainCount == 0
[p release];
NSLog(@"p.retainCount = %lu", [pretainCount]);
[prelease];//p执行后,此时对象被回收销毁,自动调用dealloc方法
重写dealloc方法
//dealloc方法,是对象的临终遗言的方法
//对象被销毁的时候,会默认的调用该方法
//注意:dealloc方法是系统自动调用的,不要手动调用
-(void)dealloc{
//先释放子类子集的对象空间
NSLog(@"Person已经挂了");
//再释放父类的
[super dealloc];
//对象被销毁的时候,会默认的调用该方法
//注意:dealloc方法是系统自动调用的,不要手动调用
-(void)dealloc{
//先释放子类子集的对象空间
NSLog(@"Person已经挂了");
//再释放父类的
[super dealloc];
}
内存管理的原则
内存管理:
对象如果不使用了,就应该回收他的空间,防止造成内存泄露
内存管理的范围:
所有的继承了NSObject的对象的内存管理,基本数据类型除外
内存管理的原则:
1、原则:只要还有人使用某个对象,该对象就不能被回收
只要你想使用这个对象,就应该让这个独享的引用计数器+1,retain
当你不想使用时,应该让对象的引用计数-1,release
2、谁创建,谁release
3、谁retain,谁release
4、总结:有始有终,有加就有减
内存管理研究的内容:
1、野指针:1)定义的指针变量没有初始化
2)指向的空间已经被释放了
2、内存泄露:栈区的P已经释放了,而指向的堆区的空间还没释放,堆区的空间就被泄露了
//创建:new、alloc、copy
Dog *bigYellowDog = [Dognew];//1
NSLog(@"bigYellowDog.retainCount = %lu", [bigYellowDogretainCount]);
Dog *jd = [bigYellowDog retain];//新的对象,两个对象地址一样
NSLog(@"jd.retainCount = %lu", [jdretainCount]);
//<Dog: 0x100206760>,<Dog: 0x100206760>
NSLog(@"%@,%@", bigYellowDog, jd);
//谁创建,谁release
[bigYellowDog release];
Dog *bigYellowDog = [Dognew];//1
NSLog(@"bigYellowDog.retainCount = %lu", [bigYellowDogretainCount]);
Dog *jd = [bigYellowDog retain];//新的对象,两个对象地址一样
NSLog(@"jd.retainCount = %lu", [jdretainCount]);
//<Dog: 0x100206760>,<Dog: 0x100206760>
NSLog(@"%@,%@", bigYellowDog, jd);
//谁创建,谁release
[bigYellowDog release];
[jdrelease];
最后,当retainCount == 0,系统自动调用dealloc方法
单个对象内存管理(野指针)
Xcode在默认情况下,为了提高效率,不会开启僵尸对象检测Zombie->Edit scheme->run->
Dog*byd = [Dognew];
[bydeat];
NSLog(@"byd.retainCount = %lu", byd.retainCount);//可以使用.调用此方法
[bydrelease];//逻辑上释放,物理上不可能删除,已经被释放的对象就是僵尸对象
//这就是野指针使用,已经释放了的空间。但是让僵尸对象去eat,不合理
//默认是不报错的,设置开启僵尸对象检测
[bydeat];
//同理,用已经释放的僵尸对象访问retainCount方法读取引用计数器的值,也是野指针使用
NSLog(@"byd.retainCount = %lu", byd.retainCount);
注意:
1、
nil:是一个对象值,赋值为空,便是nil
Nil:是一个类对象值
NULL:是一个通用指针(泛型指针)
[NSNull null]:是一个对象,用在不能使用nil的场合
2、不能使用僵尸对象,使其死而复生
[drelease];
//僵尸对象死而复生,并未开启Zombie,不会报错
[d retain];
//nil 给nil发送任何消息都没有效果
//避免使用僵尸对象的方式:对象释放完了以后,给对象赋值为nil,提高程序健壮性
[d retain];
//nil 给nil发送任何消息都没有效果
//避免使用僵尸对象的方式:对象释放完了以后,给对象赋值为nil,提高程序健壮性
d =nil;
[dretain];//再使用僵尸对象,就不会报错,不会响应任何消息
3、野指针操作
单个对象的内存泄露问题
1、创建和使用完后,没有release
2、没有遵守内存管理原则,新增的retain要和release对应
3、不当的使用nil,在release之前,使用nil赋值。导致最后并没有release,因为nil不会响应任何消息
4、在方法的内部retain,但是在main中只release一次,也满足了内存管理的原则,此时的retain比较隐蔽,要注意
多个对象的内存管理(野指针)
Person*fengjie = [Personnew];
Car *bigBen = [Car new];
bigBen.speed= 180;
//给凤姐一辆车
[fengjie setCar:bigBen];
[fengjie goLasa];
[bigBen release];//1->0 Car销毁
//这句话报错的原因 goLasa方法中使用了_car(就是bigBen)而bigBen已被销毁
//要想下面的语句不报错,必须保证_car存在
bigBen.speed= 180;
//给凤姐一辆车
[fengjie setCar:bigBen];
[fengjie goLasa];
[bigBen release];//1->0 Car销毁
//这句话报错的原因 goLasa方法中使用了_car(就是bigBen)而bigBen已被销毁
//要想下面的语句不报错,必须保证_car存在
//要想在[bigBen release]之后,_car还存在 必须bigBen的引用对象不止一个
//即至少[bigBen retain]一次,但是这种方法明显比较笨
//还可以在setCar方法里这样赋值_car = [car retain];这样比较健壮
//但是这样之后,依旧存在[bigBen release]的问题
//为了保证,人没亡,就可以调用车
//我们可以在dealloc方法里进行[bigBen release]-》[_car release]
[fengjiegoLasa];
set方法的内存管理
原则:在一个类中,有其他类的对象(关联关系)
基本数据类型作为实例变量:直接赋值_speed = speed;
对象类型作为另一个类的实例变量:先判断是否是同一个对象,然后先release旧值,在retain新值
-(void) setCar:(Car*)car {
//如果_car == car就是同一个对象
//同一个对象,release之后就不可以在retain了,否则zombie就复活了
if (_car != car) {
//先释放前一个车,release旧值
//同一个对象,release之后就不可以在retain了,否则zombie就复活了
if (_car != car) {
//先释放前一个车,release旧值
[_carrelease];
//retain新值,并且赋值给实例变量
_car = [car retain];
}
@property参数
4.4之前:
1)@property+手动实现
2)@property int age; + @synthesize age;//get和set方法的声明和实现都帮我们做了
3)@property int age; + @synthesize age = _age;//指定值赋值
4.4增强
@property int age;
1)生成_age
2)生成_age的get和set方法的声明和实现
格式:@property(参数1,参数2。。)数据类型 方法名
参数类别:
1、原子性:1)atomic:对属性加锁,多线程下线程安全,默认值
2)nonatomic:对属性不加锁,多线程下不安全,但是速度快
2、读写属性:1)readwrite:生成setter和getter方法,默认值
2)readonly:只生成getter方法
3、set方法处理:1)assign:直接赋值,默认值
2)retain:先release原来的值,再retain新值
3)copy:先release原来的值,再copy新值
替换set和get方法名称:@property(setter = isVip:,getter = isVip)
//setVip: == isVip:
//vip == isVip
//同样可以使用点语法
@class的使用
#import作用:把要引用的头文件内容,拷贝到写#import处
//如果引用的头文件内容发生变化,则引用到这个文件的所有类就需要重新编译,效率低
@class的使用:
格式:@class 类名;
@class XXX;
含义:告诉编译器,XXX是一个类,至于类有哪些属性和方法,此处不去检测
好处:如果XXX文件内容发生变化,不需要重新编译
注意:由于不知道属性和方法,所以不可以直接在其他类中去使用,如何要使用,需要在实现文件中导入该类头文件
所以,在实际开发中,.h中使用@class,.m中使用#import
1).h @class XX;
2).m #import “XX.h"
3)@class解决循环引入问题:交叉引用,循环依赖,使用#import会报错
循环retain的问题
Person*p = [Personnew];
Dog *d = [Dog new];
p.dog = d; //p,d互掐
d.owner= p;
[d release];
[d release];
[prelease];
[prelease];//虽然也可解决问题,但有可能由于顺序不同,导致某个对象多释放一次
循环的retain会导致两个对象都会内存泄露
推荐的方法:一端使用assign直接赋值,一端使用retain
NSString类的内存管理
@“abc”是字符串常量
栈区高地址,栈区-》堆区-》BSS-》数据区-》代码区
//定义字符串
//字符串的常量池
//如果你需要的字符串在常量池已经存在了,不会分配内存空间
//使用字符串的时候,
//@"abc" stringWithString alloc initWithString 都在常量区
//str2和str4如果在常量区,地址应该一样
//但是地址不一样,在堆区
NSString *str1 = @"abc";//常量区
NSString *str2 = [NSString stringWithFormat:@"aaa"];
NSString *str3 = [NSString stringWithString:@"abc"];//常量区
NSString *str4 = [[NSString alloc] initWithFormat:@"aaa"];
NSString *str5 = [[NSString alloc] initWithString:@"abc"];//常量区
NSLog(@"str1 = %@,%p,%lu", str1, str1, str1.retainCount);
NSLog(@"str2 = %@,%p,%lu", str2, str2, str2.retainCount);
NSLog(@"str3 = %@,%p,%lu", str3, str3, str3.retainCount);
NSLog(@"str4 = %@,%p,%lu", str4, str4, str4.retainCount);
NSLog(@"str5 = %@,%p,%lu", str5, str5, str5.retainCount);
危险用法:
retainCount对于系统有时候不准,自己的对象要把握retain和release呼应
while([a retainCount] > 0){
[a release];
}//死循环
autorelease基本使用
1、什么是autorelease?
自动释放池:1)在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的
2)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中
iOS5.0之后,创建方式
@autoreleasepool
{//开始
。。。。
}//结束
autorelease:是一种支持引用计数的内存管理方式
它可以暂时的保存某个对象,然后在内存池自己排干的时候对其中每个对象发release消息(每个对象只发送一次),注意,这里只是发送release消息,如果当时的retaiCount依然不为0,则该对象不会被释放。可以用该方法保存某个对象,也要注意保存之后要释放该对象。
自动释放池的使用:
1)创建自动释放池
2)加入自动释放池,对象的引用计数不会改变
[对象 autorelease];
3)自动释放池销毁,会对池子里的所有对象release一次
2、为什么会有autorelease?
OC内存管理原则:谁申请,谁释放。如果一个方法需要返回一个对象,该对象合适释放?方法内部是不会写release来释放对象的,因为这样立即释放会返回一个空对象;调用者也不会主动释放该对象,因为遵循“谁申请,谁释放”的原则。那么此时,就发生了内存泄露。
针对这种情况,OC设计了autorelease,既能保证对象正确释放,又能返回有效地对象
autorelease好处:
1)不需要关心对象释放时间
2)不需要关心什么时候调用release
3、autorelease的原理?
autorelease实际上只是把对release的调用延迟了,将对象一直保留到autoreleasepool释放时,pool中的所有对象都会调用release
4、autorelease什么时候被释放?
1)5.0之前手动释放
2)5.0之后自动释放
autorelease的注意及错误使用
1)并不是所有放到自动释放池中的代码,产生的对象就会自动释放。如果需要释放,必须加入自动释放池
Person*p = [[Personnew]autorelease];
2)如果对象调用了autorelease,但是调用的时候,没有放在任何一个自动释放池中,此对象也不会被加入自动释放池
3)尽量不要把内存较大的对象放到自动释放池中
4)不要在一个释放吃中,多次使用[p autorelease];
5)在alloc后,自动释放池结束之后,又进行[p autorelease]操作,不允许。zombie操作
autorelease的应用场景
1、快速创建对象的类方法
Person*p = [Personperson];
//person类方法:快速创建对象,并且管理对象的内存(加入自动释放池)
//1)创建一个对象 P
//person类方法:快速创建对象,并且管理对象的内存(加入自动释放池)
//1)创建一个对象 P
//2)用完之后,系统把对象释放掉
+(id) person
{
//能够创建对象
//能够帮我们把对象加入自动释放池
return [[[Person alloc] init] autorelease];
{
//能够创建对象
//能够帮我们把对象加入自动释放池
return [[[Person alloc] init] autorelease];
}
2、完善快速创建对象的方法
//创建一个学生对象
Student *stu = [Student person];//返回的是Person类型
Student *stu = [Student person];//返回的是Person类型
[sturun];//动态类型,程序运行时才知道是什么类型,其实是[Person run];
只有将类方法修改一下才可以
+(id) person
{
//能够创建对象
//能够帮我们把对象加入自动释放池
//谁调用 返回谁
//Person调用 返回[Person run];
//Student调用 返回[Student run];
//Person---》self
return [[[self alloc] init] autorelease];
{
//能够创建对象
//能够帮我们把对象加入自动释放池
//谁调用 返回谁
//Person调用 返回[Person run];
//Student调用 返回[Student run];
//Person---》self
return [[[self alloc] init] autorelease];
}
3、最终完善类方法
NSString*str = [Studentperson];
NSLog(@"str.lenth = %ld", str.length);
没完善之前,返回值类型是Student,指针类型是NSString。但是编译不会报错
将id类型改为instancetype类型:能够智能的检测指针类型和返回值类型是否一致
+(instancetype) person
{
//能够创建对象
//能够帮我们把对象加入自动释放池
//谁调用 返回谁
//Person调用 返回[Person run];
//Student调用 返回[Student run];
//Person---》self
return [[[self alloc] init] autorelease];
//能够创建对象
//能够帮我们把对象加入自动释放池
//谁调用 返回谁
//Person调用 返回[Person run];
//Student调用 返回[Student run];
//Person---》self
return [[[self alloc] init] autorelease];
}
当我们需要指定值的创建对象和初始化,我们可以
-(instancetype) initWithAge:(int)age
{
//1、先初始化父类的,并且判断是否成功
if (self = [super init]) {
//2、初始化子类
_age = age;
}
//3、返回self
return self;
}
//+(instancetype) student
//{
// return [[self alloc] initWithAge:18];
//}
//这样就写死了
+(instancetype) studentWithAge:(int)age
{
return [[self alloc] initWithAge:age];
{
//1、先初始化父类的,并且判断是否成功
if (self = [super init]) {
//2、初始化子类
_age = age;
}
//3、返回self
return self;
}
//+(instancetype) student
//{
// return [[self alloc] initWithAge:18];
//}
//这样就写死了
+(instancetype) studentWithAge:(int)age
{
return [[self alloc] initWithAge:age];
}
此时的类方法,可以传递参数
0 0
- OC内存管理-1
- OC内存管理1
- OC+1-内存管理
- OC 内存管理(1)
- 【总结】 - OC内存管理(1)
- OC--内存管理(1)
- OC内存管理-OC笔记
- OC语法<2.1>内存管理:手动内存管理1
- oc学习之旅:内存管理1
- OC内存管理
- Oc-内存管理
- OC内存管理
- OC 内存管理
- oc 内存管理
- OC内存管理
- oc -内存管理 笔记
- OC之【内存管理】
- OC内存管理小记
- C++重载类型转换操作符
- Python 值传递和引用传递
- OC-4-语法总结
- MyEclipse里运行Tomcat后,Console窗口里中文显示乱码
- 【Java】javaIO之用随机流读写文件
- OC+1-内存管理
- UIAlertController
- OC+2-ARC-Category-block
- 微信内置浏览器音频直播
- OC+3-protocol
- Tomcat【4】(tomcat在eclipse的配置)
- rtsprecorder接收rtsp流并录像存档的调试过程
- HDU 2516 取石子游戏(fibonacci博弈)
- 【Java】javaIO之带缓冲流的字节流文件读写