上篇文章对两个有关系的对象进行了内存管理的分析,本篇在上篇的基础上继续更进一步的分析多对象的内存管理和setter方法的解析。
- 首先创建两个类,Person
Person.h 文件
#import "Car.h"
@interface Person : NSObject
{
Car *_car;
}
-(void)setCar:(Car *)car;
-(Car *)car;
@end
Person.m 文件
#import "Person.h"
@implementation Person
-(void)setCar:(Car *)car{
_car = [car retain];
}
-(Car *)car{
return _car;
}
-(void)dealloc{
[_car release];
[super dealloc];
NSLog(@"---person释放了-");
}
@end
- Car类
Car.h 文件
#import <Foundation/Foundation.h>
@interface Car : NSObject
{
int _speed;
}
-(void)setSpeed:(int)speed;
-(int)speed;
@end
Car.m 文件
#import "Car.h"
@implementation Car
-(void)setSpeed:(int)speed{
_speed = speed;
}
-(int)speed{
return _speed;
}
-(void)dealloc{
NSLog(@"速度为--%d的car释放了",_speed);
[super dealloc];
}
@end
- Controller中相关操作
- (void)viewDidLoad {
[super viewDidLoad];
//p - 1
Person *p = [[Person alloc]init];
//car - 1
Car *car = [[Car alloc]init];
car.speed = 100;
//car - 2
[p setCar:car];
//car - 1
[car release];
car = nil;
//p - 0 && car = 0
[p release];
p = nil;
}
从上面的代码中最后都打印了
2017-04-16 18:28:02.537 内存管理-应用计数1[19267:395083] 速度为--100的car释放了
2017-04-16 18:28:02.538 内存管理-应用计数1[19267:395083] ---person释放了-
没毛病!
but,我们有一天发财了要换车了,好吧,那我们就换新车再看看,会不会有啥毛病呢。
- (void)viewDidLoad {
[super viewDidLoad];
//p - 1
Person *p = [[Person alloc]init];
//car - 1
Car *car = [[Car alloc]init];
car.speed = 100;
//car - 2
[p setCar:car];
//car1 - 1
Car *car1 = [[Car alloc]init];
car1.speed = 200;
//car1 - 2 换车了
[p setCar:car1];
//car - 1
[car release];
car = nil;
//car1 - 1
[car1 release];
car1 = nil;
//p - 0 && car1 = 0
[p release];
p = nil;
}
可以发现这段代码和上面的相比,我们又创建一个新车,即“car1”。这时候我们打印会发现
2017-04-16 18:58:48.624 内存管理-应用计数1[20200:419639] 速度为--200的car释放了
2017-04-16 18:58:48.626 内存管理-应用计数1[20200:419639] ---person释放了-
嗯哼,我们现在不是有三个对象嘛,按理说三个都应该释放。很简单,我们分析代码
[p setCar:car];
car是旧车这一步有问题了。此时car的引用计数已经是2了,接着我们就换车了,直到
[car release];
这一步,car的引用计数-1,此时retainCount就是1.所以它不会被释放。所以,我们在
-(void)setCar:(Car *)car{
_car = [car retain];
}
这一步应该做点事了,我们换车了,不要这个旧车了,我们就应该有责任把它注销了,所以再持有新车之前,先对旧车进行release。
-(void)setCar:(Car *)car{
[_car release];
_car = [car retain];
}
此时,我们在看结果吧
2017-04-16 19:08:35.685 内存管理-应用计数1[20617:429974] 速度为--100的car释放了
2017-04-16 19:08:35.686 内存管理-应用计数1[20617:429974] 速度为--200的car释放了
2017-04-16 19:08:35.686 内存管理-应用计数1[20617:429974] ---person释放了-
搜噶,我们要的结果出现了。但是这样就完美了吗?继续
- (void)viewDidLoad {
[super viewDidLoad];
//p - 1
Person *p = [[Person alloc]init];
//car - 1
Car *car = [[Car alloc]init];
car.speed = 100;
//car - 2
[p setCar:car];
//car - 1
[car release];
[p setCar:car];
car = nil;
//p - 0 && car1 = 0
[p release];
p = nil;
}
正如上述所示,我们在无意间在 [car release]之后又给p赋值了一次。这时,运行代码后
2017-04-16 20:27:35.446 内存管理-应用计数1[22629:480814] 速度为--100的car释放了
2017-04-16 20:27:35.446 内存管理-应用计数1[22629:480814] *** -[Car retain]: message sent to deallocated instance 0x60800001f010
(lldb)
报错了。why?
我们分析代码
//car - 2
[p setCar:car];
//car - 1
[car release];
这里的p持有了新车之后会到
-(void)setCar:(Car *)car{
[_car release];
_car = [car retain];
}
这里_car就是现在的车,先进行了release,之后他的引用计数成了1,接着又 [car release]之后引用计数器就成了0.我们接着给
[p setCar:car];
这里的car还是以前的_car,以前的car已经引用计数是0了。我们在给发送retain消息,就成了僵尸对象了,所以报错了。
- 那么解决方法呢?
要是传进来的car和当前的car是一样的,那就不用在执行里面的操作,那我们就判断
-(void)setCar:(Car *)car{
if (_car != car) {
[_car release];
_car = [car retain];
}
}
这样的话在运行,结果
2017-04-16 20:40:23.719 内存管理-应用计数1[23174:495402] 速度为--100的car释放了
2017-04-16 20:40:23.720 内存管理-应用计数1[23174:495402] ---person释放了-
ok了吧。这样就解决了可能会发生的问题。
网友评论