美文网首页@IT·互联网程序员教育
内存管理-setter方法的解析

内存管理-setter方法的解析

作者: Mr_HeH | 来源:发表于2017-04-16 20:44 被阅读51次

    上篇文章对两个有关系的对象进行了内存管理的分析,本篇在上篇的基础上继续更进一步的分析多对象的内存管理和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了吧。这样就解决了可能会发生的问题。

    相关文章

      网友评论

        本文标题:内存管理-setter方法的解析

        本文链接:https://www.haomeiwen.com/subject/bjgaattx.html