黑马程序员技术交流社区

标题: OC野指针和空指针,僵尸对象和内存泄露 [打印本页]

作者: 彭智林老师    时间: 2016-5-25 01:46
标题: OC野指针和空指针,僵尸对象和内存泄露
本帖最后由 彭智林老师 于 2016-5-25 08:41 编辑

           哈喽,大家好,我是深圳双元课堂的iOS导师--彭智林老师,我觉得我们每一个来到黑马的老师或者学生,都在这苦逼的路上逗逼的奔向牛逼,因为我们每一天都在努力,每一天都在进步,每一天都在用心,每天都在严格要求自己,而且人生嘛,只有拼出来的精彩,绝无等待出来的辉煌,所以我们首先呢我们应该向自己和身边的每一个努力的小伙伴致敬对不对?。接下来呢,我给大家分享一点知识,我最近和学生交流,发现部分同学对野指针和空指针、内存泄露这块理解起来有点费劲,其实这块不难,这个地方呢概念比较多,所以只是需要大家花点时间理解,那么如果现在还有不明白的呢,我给大家做了一个简单的总结,大家可以参考一下,希望会有点帮助,咋们大家一起加油


  空指针、野指针和僵尸对象、内存泄露


   一.什么是空指针、野指针和僵尸对象、内存泄露


    1.空指针
    1> 没有存储任何内存地址的指针就称为空指针(NULL指针)  
    2> 空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0。
下面是2个代码示例
  1.   Student *s1 = NULL;

  2.   Student *s2 = nil;
复制代码

     2.  野指针
   1. C语言中的野指针: 指的是声明1个指针变量.没有为这个指针变量初始化.  
        那么这个指针变量的值就是1个垃圾值.   
        指向内存中随机的1块空间,这个指针就叫做野指针.

    2. OC中的野指针:  1个指针指向的对象已经被释放了.那么这个指针就叫做野指针

3.  僵尸对象
>  指的是一个已经被回收 但是 这个对象的数据还在内存中.
    像这样的对象就叫做僵尸对象.

4.内存泄露

"内存泄露"  (栈区的指向已经释放,  堆区的空间没有释放, 这时堆区的空间就被泄露了)
堆区,需要程序员手动管理


不管是多个对象还是单个对象,只要我们研究它的内存管理,就两点:

1.僵尸对象(野指针)
2.内存泄露

  二.野指针、空指针的例子









   2. 自定义Student类,在main函数中添加下列代码
  1. int main(int argc, const char * argv[]) {
  2.     @autoreleasepool {
  3.         HMStudent *stu = [[HMStudent alloc]init];
  4.         [stu setAge:18];
  5.         [stu release];
  6.         [stu setAge:20];
  7.     }
  8.     return 0;
  9. }
复制代码

   3.接下来分析一下报错的原因
  
   1> 执行完第3行代码后,内存中有个指针变量stu,指向了HMStudent对象
  1. HMStudent *stu = [[HMStudent alloc]init];
复制代码





    2> 接下来是第4行代码
  1. [stu setAge:18];
复制代码
  这行代码的意思是:给stu所指向的HMStudent对象发送一条setAge:消息,即调用这个HMStudent对象的setAge:方法。目前来说,这个HMStudent对象仍存在于内存中,所以这句代码没有任何问题。

    3> 接下来是第5行代码
  1. [stu release];
复制代码
这行代码的意思是:给stu指向的HMStudent对象发送一条release消息。在这里,HMStudent对象接收到release消息后,会马上被销毁,所占用的内存会被回收。
HMStudent对象被销毁了,地址为0x10010的内存就变成了"垃圾内存",然而,指针变量stu仍然指向这一块内存,这时候,stu就称为了野指针!

    4> 最后执行了第6行代码
  1. [stu setAge:20];
复制代码
这句代码的意思仍然是: 给stu所指向的HMStudent对象发送一条setAge:消息。但是在执行完第5行代码后,HMStudent对象已经被销毁了,它所占用的内存已经是垃圾内存,如果你还去访问这一块内存,那就会报野指针错误。这块内存已经不可用了,也不属于你了,你还去访问它,肯定是不合法的。所以,这行代码报错了!

    4.如果改动一下代码,就不会报错
  1. #import <Foundation/Foundation.h>
  2. #import "HMStudent.h"
  3. int main(int argc, const char * argv[]) {
  4.     @autoreleasepool {
  5.         
  6.         HMStudent *stu = [[HMStudent alloc]init];
  7.         
  8.         [stu setAge:18];
  9.         
  10.         [stu release];
  11.         
  12.         stu = nil;
  13.         
  14.         [stu setAge:20];
  15.         
  16.         
  17.     }
  18.     return 0;
  19. }
复制代码
那么这个时候,stu变成了空指针,stu就不再指向任何内存了
因为stu是个空指针,没有指向任何对象,因此[stu setAge:20]消息是发不出去的,不会造成任何影响。当然,肯定也不会报错。

    5.总结
1> 利用野指针发消息是很危险的,会报错。也就是说,如果一个对象已经被回收了,就不要再去操作它,不要再尝试给它发消息。
2> 利用空指针发消息是没有任何问题的,也就是说下面的代码是没有错误的:
  1. [nil setAge:20];
复制代码
#  避免使用僵尸对象的方法
1)僵尸对象调用方法,会报错,访问成员变量有时是可以的(但是这个是未知的)。
2)为了防止不小心调用了僵尸对象,可以将对象赋值nil(对象的空值)
用nil调用方法是不会报错的。
# 对象的内存泄露的几种情况
1) retain和release不匹配,retain多余release导致的内存泄露;
2) 对象使用过程中,没有被release,而被赋值为nil;
3) 在方法中不当的使用了retain;





作者: xushengbin    时间: 2016-5-25 09:15
干货
作者: 橘子哥    时间: 2016-5-26 10:51
厉害,必须赞
作者: 王博1    时间: 2016-5-26 10:59
老师把一些基本概念讲的很好,点赞{:2_30:}
作者: 彭智林老师    时间: 2016-5-26 12:53
xushengbin 发表于 2016-5-25 09:15
干货

彬哥的C指针艺术也都是干货,学习了,必须赞
作者: 彭智林老师    时间: 2016-5-26 12:53
xushengbin 发表于 2016-5-25 09:15
干货

彬哥的C指针艺术也都是干货,学习了,必须赞
作者: 彭智林老师    时间: 2016-5-26 12:55
橘子哥 发表于 2016-5-26 10:51
厉害,必须赞

橘子哥的帖子,也狠赞
作者: 彭智林老师    时间: 2016-5-26 12:56
王博1 发表于 2016-5-26 10:59
老师把一些基本概念讲的很好,点赞


作者: 程清林    时间: 2016-5-26 14:01
干活   赞一个
作者: 贪睡的猫    时间: 2016-5-26 14:48
虽然没看懂,但是我收到了鼓舞
作者: 夜月    时间: 2016-5-26 15:35
高大上。
作者: 杨文君老师    时间: 2016-5-26 16:20
给力,貌似看到了植物大战僵尸的赶脚{:3_53:}
作者: 张可可    时间: 2016-5-26 23:03
大家好  过来 冒个泡
作者: 765237684    时间: 2016-5-26 23:04
厉害,有学到了精华的知识了
作者: 765237684    时间: 2016-5-26 23:06
这真是长见识啊每天都有好多知识给力
作者: cxc0603    时间: 2016-5-26 23:27
好厉害的感觉
作者: 921717549    时间: 2016-5-26 23:43
赞一个66666666
作者: 之约    时间: 2016-5-27 00:40
谢谢分享!
作者: monkey001    时间: 2016-5-27 10:10
虽然没看懂,但是我收到了鼓舞
作者: 牛牛很牛    时间: 2016-5-28 22:29
多谢老师分享经验
作者: 秦恺    时间: 2016-5-28 22:41
好给力 赞一个
作者: ☆☆☆_tffV4    时间: 2016-5-28 22:44
赞一个,清楚多了
作者: jdc123    时间: 2016-5-29 11:08
老师可以讲一下设置了快速构造方法时,NSString属性的赋值是用copy么,用不用考虑retain和release
作者: 767784276    时间: 2016-5-30 23:08
66666666666
作者: 1046656214    时间: 2016-5-30 23:23
讲解很到位,虽然现在基础薄弱,但像"僵尸对象"和"空指针"还能看懂点,挺好的讲解
作者: 彭智林老师    时间: 2016-5-31 00:12
jdc123 发表于 2016-5-29 11:08
老师可以讲一下设置了快速构造方法时,NSString属性的赋值是用copy么,用不用考虑retain和release ...

用copy比用retain安全,当是NSString的时候,其实用copy和retain都行,当用NSMutableString,那么就要用copy,使得NSMutableString的值不会被修改,而用retain的时候,NSMutableString的值会被修改。
举个例子
  1. @interface person : NSObject<NSCopying>
  2. @property (nonatomic,copy)NSString * name;
  3. @end
复制代码
  1. person * p  = [[person alloc]init];
  2.    
  3.     NSMutableString * name = [[NSMutableString alloc]initWithString:@"hello"];
  4.     p.name = name;
  5.    
  6.     [name appendString:@" word"];
  7.     NSLog(@"%@",p.name);
复制代码

可以发现打印结果还是hello;

如果你的name属性是用retain修饰,则打印的是hello word;
作者: 彭智林老师    时间: 2016-5-31 00:12
jdc123 发表于 2016-5-29 11:08
老师可以讲一下设置了快速构造方法时,NSString属性的赋值是用copy么,用不用考虑retain和release ...

用copy比用retain安全,当是NSString的时候,其实用copy和retain都行,当用NSMutableString,那么就要用copy,使得NSMutableString的值不会被修改,而用retain的时候,NSMutableString的值会被修改。
举个例子:
  1. @interface person : NSObject<NSCopying>
  2. @property (nonatomic,copy)NSString * name;
  3. @end
复制代码
  1. person * p  = [[person alloc]init];
  2.    
  3.     NSMutableString * name = [[NSMutableString alloc]initWithString:@"hello"];
  4.     p.name = name;
  5.    
  6.     [name appendString:@" word"];
  7.     NSLog(@"%@",p.name);
复制代码

可以发现打印结果还是hello;

如果你的name属性是用retain修饰,则打印的是hello word;
作者: 彭智林老师    时间: 2016-5-31 00:12
jdc123 发表于 2016-5-29 11:08
老师可以讲一下设置了快速构造方法时,NSString属性的赋值是用copy么,用不用考虑retain和release ...

用copy比用retain安全,当是NSString的时候,其实用copy和retain都行,当用NSMutableString,那么就要用copy,使得NSMutableString的值不会被修改,而用retain的时候,NSMutableString的值会被修改。
举个例子
  1. @interface person : NSObject<NSCopying>
  2. @property (nonatomic,copy)NSString * name;
  3. @end
复制代码
  1. person * p  = [[person alloc]init];
  2.    
  3.     NSMutableString * name = [[NSMutableString alloc]initWithString:@"hello"];
  4.     p.name = name;
  5.    
  6.     [name appendString:@" word"];
  7.     NSLog(@"%@",p.name);
复制代码

可以发现打印结果还是hello;

如果你的name属性是用retain修饰,则打印的是hello word;
作者: 111111111111    时间: 2016-5-31 10:17
66666666666666666666
作者: jdc123    时间: 2016-5-31 13:17
彭智林老师 发表于 2016-5-31 00:12
用copy比用retain安全,当是NSString的时候,其实用copy和retain都行,当用NSMutableString,那么就要用copy, ...

谢谢老师解答,关于快速构造方法我还是有些不明白,比如你举得这个例子,在ARC机制下,类声明时候用@proerty(nonatomic,copy)NSString *name定义属性,而我有快速设定对象属性初值的需求(利用重写构造方法,在对象申请时候直接给属性赋值那种),在快速构造方法实现时候,我用不用考虑内存问题,是这样重构
if(self=[super init]){
if(_name!=name){
[_name release];
_name = [name copy];}
return self;
}还是直接赋值
if(self=[super init]){
_name = [name copy];
return self;}





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2