IOS开发基础Object-C(08)—OC内存管理(2)-对象之间的内存管理
2015-11-22 07:37
615 查看
前几天,我们学习了内存管理的基本知识,了解了内存管理的基本原理。那么,今天我们来学习一下对象之间的内存管理,看看对象之间是如何进行内存管理的。首先,我们新建两个类:Student和Book类,在Student类中声明一个Book对象
Student.h
Student .m
我们再来进行Book类的声明与实现
Book.h
Book.m
main.m
运行结果:
2015-10-21 16:17:58.316 对象之间的内存管理[2049:303] book 3.500000被销毁了
2015-10-21 16:17:58.318 对象之间的内存管理[2049:303] student 10被销毁了
似乎没有什么问题,Student和Book都被释放了。但是真的没有什么问题吗?在实际开发中,我们通常把一些功能抽出来单独写一个方法。比如我们现在加一个test方法把Book的功能抽出来,大家再看看有没有什么问题。
大家有没有发现问题?那我们再来运行一下
2015-10-21 16:27:58.396 对象之间的内存管理[2349:303]
student 10被销毁了
出现内存泄露了,book对象没有被释放,也就是说,book没有被release
那么有同学就会说了,在test方法中,在
test1() 。我们来看一下
在Student.h文件中声明
mian.m函数修改如下:
注意:
既然知道问题了,那问题就好解决了
解决方法:
我们可以使retain对象,使计数器+1,至于在哪里retain,我们可以遵循一个原则:谁调用对象谁retain,Student的stu要使用book对象,那就让Student自己在setBook中retain最好
好,解决了野指针的问题,但是对于内存泄露还没有解决,那么我们在哪里release呢?test1中?肯定不行,因为test1()中没有retain,new或者alloc等创建对象的语法,release的话违背了我们“谁创建谁释放“的原则。既然stu对象想使用book对象,你就应该在retain完成后释放它,而不应该把它交给test1()去release。至于在Student对象的什么时候释放最好呢?当然是在stu对象结束退出之后,stu对象都不存在了,book对象就更没有存在的必要了。所以在Student对象的dealloc中释放掉book对象最合适。
这个问题已经完美解决了,那我们来看下一个问题。假如我在test()方法中新创建一个对象book2,调用initWithPrice改变price的值
这样大家看一下,有没有内存泄露,这个大家要根据引用计数进行分析,引用计数为0时进行释放。那我们来运行一下
2013-10-21 17:43:01.519 对象之间的内存管理[2743:303]当前读的书的价格是:4.500000
2013-10-21 17:43:01.521 对象之间的内存管理[2743:303] book 4.500000被销毁了
2013-10-21 17:43:01.523 对象之间的内存管理[2743:303] student 10被销毁了
book 3.500000这本书没有被销毁,为什么没有被销毁?
大家先不要看下边的,先自己想一想
大家学软件编程一定要有自己独立思考和分析问题的能力,这样大家才能走的更高更远,当然这只是我个人的一点浅见,毕竟我也是一个想要飞的菜鸟。
那废话就不多说了,我直接告诉大家
在stu.book=book2时,调用了setBook方法又进行了一次retain,这时候引用计数器为2,但是在最后释放调用dealloc方法时,仅仅进行了一次release,所以最后引用计数器还是为1,造成了内存泄露。
所以我们可以改进一下:
我们可以先把旧的成员释放了,在retain新传进来的对象,这样就没有问题了
但是仔细想想还是有一点小瑕疵,假如,我在test()方法中不小心多写了一句stu.book=book;
此时,stu.book 又再一次调用setter函数,在setter函数中release了book,问题是此时的book对象和_book对象时一样的,book对象被释放了(即_book指向的内存也不存在了),_book对象又再一次retian操作,就会造成野指针。
所以,要判断一下传进来的对象是否为当前对象,如果是当前对象的话就没有必要再一次release,修改如下:
完美的内存管理,这样就是一段很完美的代码了
视频相关链接
http://pan.baidu.com/s/1jGLbz06
Student.h
#import <Foundation/Foundation.h> #import "Book.h" @interface Student : NSObject { int age; Book *book; } @property int age; - (id) initWithAge:(int)_age; @property Book *book; @end
Student .m
#import "Student.h" @implementation Student @synthesize age, book;//在Xcode4.5及以后的版本中,这一句可以省略,编译器会自动实现getter和setter方法 #pragma mark 构造方法 - (id)initWithAge:(int)_age{ if(self = [super init]) age = _age; return self; } #pragma mark 回收对象 - (void)dealloc{ NSLog(@"student %i 被销毁了", age); [super dealloc]; //不要忘了这一句,而且是放在最后的。 } @end
我们再来进行Book类的声明与实现
Book.h
#import <Foundation/Foundation.h> @interface Book : NSObject { float price; } @property float price; - (id)initWithPrice:(float)_price; @end
Book.m
#import "Book.h" @implementation Book @synthesize price;//在Xcode4.5及以后的版本中,这一句可以省略,编译器会自动实现getter和setter方法 - (id)initWithPrice:(float)_price{ if(self = [super init]) price = _price; return self; } - (void)dealloc{ NSLog(@"book %f 被销毁了", price); [super dealloc];} @end
main.m
#import <Foundation/Foundation.h> #import "Student.h" #import "Book.h" int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu=[[Student alloc]initWithAge:10]; Book *book=[[Book alloc]initWithPrice:3.5]; stu.book=book; [book release]; [stu release]; } return 0; }
运行结果:
2015-10-21 16:17:58.316 对象之间的内存管理[2049:303] book 3.500000被销毁了
2015-10-21 16:17:58.318 对象之间的内存管理[2049:303] student 10被销毁了
似乎没有什么问题,Student和Book都被释放了。但是真的没有什么问题吗?在实际开发中,我们通常把一些功能抽出来单独写一个方法。比如我们现在加一个test方法把Book的功能抽出来,大家再看看有没有什么问题。
#import <Foundation/Foundation.h> #import "Student.h" #import "Book.h" void test(Student *stu){ Book *book = [[Book alloc] initWithPrice:3.3]; stu.book = book; } int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] initWithAge:10]; test(stu); [stu release]; } return 0; }
大家有没有发现问题?那我们再来运行一下
2015-10-21 16:27:58.396 对象之间的内存管理[2349:303]
student 10被销毁了
出现内存泄露了,book对象没有被释放,也就是说,book没有被release
那么有同学就会说了,在test方法中,在
stu.book=book;后面加上一个
[book release];不就可以解决了吗?好像也没有什么问题,也没有违背内存释放的原则(谁创建谁释放)。如果再增加一个新的需求,我们再给Student类增加一个方法叫readBook( ),用于打印出学生当前读的书的价格。新建一个test1()方法,调用readBook()方法,然后再在main函数中调用
test1() 。我们来看一下
在Student.h文件中声明
-(void)readBook;在Student.m文件中实现
#pragma mark 读书 - (void)readBook{ NSLog(@"当前读的书的价格是:%f",book.price); //注意,这里调用了book.prise }
mian.m函数修改如下:
#import <Foundation/Foundation.h> #import "Student.h" #import "Book.h" void test(Student *stu){ Book *book = [[Book alloc] initWithPrice:3.3]; stu.book = book; [book release]; } void test1(Student *stu){ [stu readBook]; } int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] initWithAge:10]; test(stu); test1(stu); [stu release]; } return 0; }
注意:
[stu readBook];调用的是
#pragma mark 读书 - (void)readBook{ NSLog(@"当前读的书的价格是:%f",book.price); //注意,这里调用了book.prise }介绍这里大家有没有看出问题,是不是出现了问题?在test()中,我们已经把book释放了,但是在test1()中,我们调用了readBook()方法,再次调用了book.price。既然book对象已经释放了,已经不存在了,那我们访问不存在的内存对象会发生什么错误?对,野指针错误!大家不要嫌我啰嗦,这个过程大家一定要明白,也一定要会分析对象的引用计数。要不然到最后会很麻烦的,经常出现一大堆莫名其妙的错误。
既然知道问题了,那问题就好解决了
解决方法:
我们可以使retain对象,使计数器+1,至于在哪里retain,我们可以遵循一个原则:谁调用对象谁retain,Student的stu要使用book对象,那就让Student自己在setBook中retain最好
-(void)setBook:(Book *)_book{ book=[_book retain]; }
好,解决了野指针的问题,但是对于内存泄露还没有解决,那么我们在哪里release呢?test1中?肯定不行,因为test1()中没有retain,new或者alloc等创建对象的语法,release的话违背了我们“谁创建谁释放“的原则。既然stu对象想使用book对象,你就应该在retain完成后释放它,而不应该把它交给test1()去release。至于在Student对象的什么时候释放最好呢?当然是在stu对象结束退出之后,stu对象都不存在了,book对象就更没有存在的必要了。所以在Student对象的dealloc中释放掉book对象最合适。
#pragma mark 回收对象 - (void)dealloc{ //释放book对象 [book release]; NSLog(@"student %i 被销毁了", age); [super dealloc]; }
这个问题已经完美解决了,那我们来看下一个问题。假如我在test()方法中新创建一个对象book2,调用initWithPrice改变price的值
void test(Student *stu){ Book *book = [[Book alloc] initWithPrice:3.5]; stu.book = book; [book release]; Book *book2 = [[Book alloc] initWithPrice:4.5]; stu.book = book2; [book2 release]; }其他都保持不动,为了方便大家阅读,我们把main方法考过来
int main(int argc, const char * argv[]) { @autoreleasepool { Student *stu = [[Student alloc] initWithAge:10]; test(stu); test1(stu); [stu release]; } return 0; }
这样大家看一下,有没有内存泄露,这个大家要根据引用计数进行分析,引用计数为0时进行释放。那我们来运行一下
2013-10-21 17:43:01.519 对象之间的内存管理[2743:303]当前读的书的价格是:4.500000
2013-10-21 17:43:01.521 对象之间的内存管理[2743:303] book 4.500000被销毁了
2013-10-21 17:43:01.523 对象之间的内存管理[2743:303] student 10被销毁了
book 3.500000这本书没有被销毁,为什么没有被销毁?
大家先不要看下边的,先自己想一想
大家学软件编程一定要有自己独立思考和分析问题的能力,这样大家才能走的更高更远,当然这只是我个人的一点浅见,毕竟我也是一个想要飞的菜鸟。
那废话就不多说了,我直接告诉大家
在stu.book=book2时,调用了setBook方法又进行了一次retain,这时候引用计数器为2,但是在最后释放调用dealloc方法时,仅仅进行了一次release,所以最后引用计数器还是为1,造成了内存泄露。
所以我们可以改进一下:
- (void)setBook:(Book *)_book{ //先释放旧的成员变量 [book release]; //再retain新传进来的对象 book = [_book retain]; }
我们可以先把旧的成员释放了,在retain新传进来的对象,这样就没有问题了
但是仔细想想还是有一点小瑕疵,假如,我在test()方法中不小心多写了一句stu.book=book;
void test(Student *stu){ Book *book = [[Book alloc] initWithPrice:3.3]; stu.book = book; [book release]; stu.book = book;}
此时,stu.book 又再一次调用setter函数,在setter函数中release了book,问题是此时的book对象和_book对象时一样的,book对象被释放了(即_book指向的内存也不存在了),_book对象又再一次retian操作,就会造成野指针。
所以,要判断一下传进来的对象是否为当前对象,如果是当前对象的话就没有必要再一次release,修改如下:
- (void)setBook:(Book *)_book{ if(_book != book){ //先释放旧的成员变量 [book release]; //再retain新传进来的对象 book = [_book retain]; } }
完美的内存管理,这样就是一段很完美的代码了
视频相关链接
http://pan.baidu.com/s/1jGLbz06
相关文章推荐
- Lua的内存管理浅析
- 跟我学习JScript的Bug与内存管理
- 深入探讨PHP中的内存管理问题
- linux 内存管理机制详细解析
- 解析PHP中的内存管理,PHP动态分配和释放内存
- javascript内存管理详细解析
- JavaScript内存管理介绍
- Cocos2d-x的内存管理总结
- 模拟实现C语言中的内存管理
- javascript错误的认识不用关心内存管理
- IOS中内存管理那些事
- OC - 9.基于Quartz2D绘制下载进度条(demo)
- Python深入学习之内存管理
- 简单说说STL的内存管理
- iOS开发之详谈属性设置readwrite、readonly、retain、copy、assign、nonatomic
- InnoDB存储引擎的内存管理机制
- jemalloc优化MySQL、Nginx内存管理
- 【转】JVM内存管理:深入垃圾收集器与内存分配策略
- JavaScript内存管理介绍
- 查询linux发行版本号方法总结