A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

彭智林老师

初级黑马

  • 黑马币:26

  • 帖子:12

  • 精华:0

本帖最后由 彭智林老师 于 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.内存泄露

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


  • 以下是几个简单的例子对比一下野指针和空指针的区别  


  • 首先打开Xcode改为MRC内存管理和打开僵尸僵尸对象检测





   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. }
复制代码
  • 运行程序,你会发现运行到[stu setAge:20]这句代码会报错,是个野指针错误

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




  • 假设HMStudent对象的地址为0x10010    ,指针变量stu的地址为0xhhh045  ,stu中存储的是HMStudent对象的地址0x10010。即指针变量stu指向了这个HMStudent对象。

    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;




29 个回复

正序浏览
彭智林老师 发表于 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;}
回复 使用道具 举报
66666666666666666666
回复 使用道具 举报
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;
回复 使用道具 举报
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;
回复 使用道具 举报
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;
回复 使用道具 举报
讲解很到位,虽然现在基础薄弱,但像"僵尸对象"和"空指针"还能看懂点,挺好的讲解
回复 使用道具 举报
66666666666
回复 使用道具 举报
老师可以讲一下设置了快速构造方法时,NSString属性的赋值是用copy么,用不用考虑retain和release
回复 使用道具 举报
赞一个,清楚多了
回复 使用道具 举报
好给力 赞一个
回复 使用道具 举报
多谢老师分享经验
回复 使用道具 举报
虽然没看懂,但是我收到了鼓舞
回复 使用道具 举报
谢谢分享!
回复 使用道具 举报
赞一个66666666
回复 使用道具 举报
好厉害的感觉
回复 使用道具 举报
这真是长见识啊每天都有好多知识给力
回复 使用道具 举报
厉害,有学到了精华的知识了
回复 使用道具 举报
大家好  过来 冒个泡
回复 使用道具 举报
给力,貌似看到了植物大战僵尸的赶脚{:3_53:}
回复 使用道具 举报
高大上。
回复 使用道具 举报
12下一页
您需要登录后才可以回帖 登录 | 加入黑马