zoukankan      html  css  js  c++  java
  • Objective-C 【单个对象内存管理(野指针&内存泄露)】

    ———————————————————————————————————————————
    单个对象内存管理

    (1)野指针
    ①定义了一个指针变量,但是并没有赋初值,它随机指向一个东西
    ②某指针变量指向的内存空间被释放掉了(指向僵尸对象的指针)

    (2)僵尸对象
    已经被销毁的对象(无法被使用的对象)

    (3)空指针
    没有指向存储空间的指针(里面存的是nil,也就是0)
    给空指针发消息是没有任何反应的,不会提示出错



    代码:

    #import <Foundation/Foundation.h>

    @interface Person : NSObject
    -(void)run;
    @end

    @implementation Person
    -(void)run
    {
        NSLog(@"run!");
    }
    - (void)dealloc
    {
        NSLog(@"retainCount的结果为0,对象内存被释放!");
        [super dealloc];
    }
    @end

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *p=[[Person alloc]init];
            NSLog(@"[p retainCount]=%tu",[p retainCount]);  //值为1
            
            [p run];
            NSLog(@"[p retainCount]=%tu",[p retainCount]);  //值还是为1,当前只有一个使用对象p的,也就是他自己,引用对象p的次数并没有增加
            
            [p retain];
            NSLog(@"[p retainCount]=%tu",[p retainCount]);  //值为2
            
            [p release];
            NSLog(@"[p retainCount]=%tu",[p retainCount]);  //值为1
            
            [p release];//这里的计数器值为0,内存被释放
            
     //       [p run];    //内存释放之后run方法还是可以调用的
    //        那么问题来了,上面run方法是在对象p的堆区内存被释放之后调用的,为什么还会调用成功呢?现在我们就来解决也是上节课遗留的问题
    //        首先当p对象被release之后(内存被释放),p就是一个野指针(僵尸对象)了
    //        我们知道,虽然p在堆区的内存空间被释放了,但是内存空间是不会变的还在那儿,栈区中也存在p。只是p已经失去了那一部分内存的管理权。如果此时你非要去使用这块空间的话,且这块内存空间没有分配给其他的程序,其实还是能获取到内容的。但是如果这块释放了的空间又被分配给其他的变量或者程序,这时候使用就不对了。
    //        而且还有一点,就是上面释放之后又调用run方法,不一定能百分百的调用,可能10次会出现一次调用错误的情况,这也就是野指针的危险。所以说,就不能用僵尸对象调用!!调用是完全无意义的!
            
    //        [p retain];
    //        这样是完全错误的,我们上面已经将p所指向的对象内存释放,那么该对象就成为了僵尸对象,我们就不能够这样让其复活(这样硬来得到的值也是错误的结果)
    //        为了避免我们使用僵尸对象,我们可以做这样一个处理
             p=nil;
    //        这样处理过后,编译器在执行下列语句的时候就不会报错了。
            [p run];//不会执行,因为p为nil
            [p retain];//也不会让计数+1,还是因为p为nil(空值)
             NSLog(@"[p retainCount]=%tu”,p.retainCount);
    //点语法(  这里相当于将p.retainCount——>[p  retainCount]  )

        }
        return 0;
    }


    ★那么我们应该怎么防止野指针的调用呢?




    操作步骤就是上面的两幅图了,将第二幅图标记的地方打勾,那么再使用野指针运行就会报错了!
    Enable Zombie Objects 就是开启僵尸模式~


    ★★★关于nil和Nil以及NULL的区别

    nil:首先这是一个空指针,而且这是一个OC的对象,是一个对象值,如果我们把一个对象设为空的话,我们就要设为nil  (  #define nil ((id) 0)   )

    Nil:这是一个类对象值,如果把一个类对象设为空,那么我们就要设为Nil

    NULL:是一个通用指针(泛型指针) (    #define NULL ((void *) 0 )   )

    NSNull: [ NSNull  null ]  是一个对象,用在不能使用nil的场合


    还有最后一点我们 需要注意的,那就是当我们释放了一个对象的内存空间,让该对象成为了僵尸对象,那么是不能够  用 [p  retain];  让它复活的。在上面的程序中有体现。


    (4)内存泄漏

    代码:

    #import <Foundation/Foundation.h>

    @interface Car : NSObject
    -(void)run:(Car *)cccc;
    @end

    @implementation Car
    - (void)dealloc
    {
        NSLog(@"释放内存!");
        [super dealloc];
    }

    -(void)run:(Car *)cccc
    {
        NSLog(@"车在跑");
        [cccc retain];//在方法内部使用retain让cccc的计数+1
    }
    @end

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
    //        (4)内存泄漏问题
            
    //        ① retain 的次数 和 release 的次数 不匹配
            
    //        Car *car=[Car new];
    //        NSLog(@"car->retainCount=%tu",car.retainCount);//1
    //        
    //        [car retain];
    //        [car retain];
    //        [car retain];
    //        
    //        NSLog(@"car->retainCount=%tu",car.retainCount);//4
    //        
    //        [car release];
    //        
    //        NSLog(@"car->retainCount=%tu",car.retainCount);//3
    ////        显然  retain 的次数 和 release 的次数 是不匹配的。retainCount 不为0,那么空间自然没有被回收释放
    ////        如果不想让内存泄漏,那么必须 retain + new = release  (增加引用计数=减少引用计数)
            
    ////        ②对象在使用过程中被赋值了nil
            
    //        Car *car1=[Car new];
    //        
    //        [car1 retain];
    //        [car1 retain];
    //        [car1 retain];
    //        NSLog(@"car1->retainCount=%tu",car1.retainCount);//4
    //        
    //        car1=nil;
    //        [car1 release];//其实这四条release语句是没有作用的 ,是 [nil release]; ,我们知道向nil发送什么指令都不会报错,但是此时原对象car1的空间是没有被释放的,所以说最后car1的内存还是被泄漏了。
    //        [car1 release];
    //        [car1 release];
    //        [car1 release];
    //        
    //        NSLog(@"car1->retainCount=%tu",car1.retainCount);//0,此时的0说明不了任何问题,并不是我们成功释放内存,也不会打印dealloc中的语句
            
    //        ③在方法中不当的使用了retain
            
    //        Car *car2=[Car new];
    //        [car2 run:car2];//我们将 car2 自己作为参数传进去run方法,然后在run方法的内部retain,那么car2->retainCount=2,所以说这里如果只release是不够的,应该release两次才能释放完内存
        }
        return 0;
    }


    ———————————————————————————————————————————

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    C/C++ 链接汇总
    C# 网页操作
    Win10屏幕开始菜单图标丢失修复方案无意中发现的...
    AI 人工智能
    GIT 命令
    Layui / WEB UI
    PHP DES解密 对应Java SHA1PRNG方式加密
    视频参数介绍及关系《转》
    分布式时系统/服务 数据一致性方案
    JS埋点 小结
  • 原文地址:https://www.cnblogs.com/wzy294250051/p/4787885.html
Copyright © 2011-2022 走看看