码迷,mamicode.com
首页 > 其他好文 > 详细

OC的内存管理

时间:2015-05-14 15:41:44      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:

在OC中当一个APP使用的内存超过20M,则系统会向该APP发送 Memory Warning消息,收到此消息后,需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等,否则程序会崩溃

OC内存管理的范围管理范围:  

  管理任何继承NSObject的对象,对其他的基本数据类型无效

  本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放与栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露

  new创建在堆区,所以我们主要是对堆区内的内容进行管理

  

OC内存管理的原理

1)对象的所有权及引用计数
    对象所有权概念
        任何对象都可能拥有一个或多个所有者.只要一个对象至少还拥有一个所有者,它就会继续存在
  Cocoa所有权策略
     任何自己创建的对象都归自己所有,可以使用名字以"alloc"或"new"开头或名字中包含"copy"的方法创建对象,可以使用retain来获得一个对象的所有权
2)对象的引用计数器
     每个OC对象都有自己的计数器,是一个整数表示对象被引用的次数,即现在有多少东西在没用这个对象.对象刚被创建时,默认计数器值为1,当计数器的值变为0时,则对象销毁.

  在每个OC对象内部,都专门有4个字节的存储空间来存储引用计数器.retainCount
3)引用计数器的作用
判断对象要不要回收的唯一依据(存在一种例外:对象值为nil时,引用计数为0,但不回收空间)就是计数器是否为0,若不为0则存在
4)对引用计数器的操作
  给对象发送消息,进行相应的计数器操作.
retain消息:使计数器+1,该方法返回对象本身
  release消息;使计数器-1(并不代表释放对象)
  retainCount消息,获得对象当前的引用计数器 使用%ld / %tu打印
5)对象的销毁
  当一个对象的引用计数器为0时,那么它将被销毁,其占用的内存被系统回收.
当对象被销毁时,系统会自动向对象发送一条dealloc消息,一般会重写dealloc方法,在这里释放相关的资源,dealloc就像是对象的"临终遗言"。
  一旦重写了dealloc方法就必须调用[super dealloc],并且放在代码块的最后调用(不能直接调用dealloc方法).一旦对象被回收了,那么他所占用的存储空间就不再可用,坚持使用会导致崩溃(野指针错误)

注意:
  1)如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就补可能被回收(除非整个程序已经退出)
  2)任何一个对象,刚生下来的时候,引用计数器都为1.(对象一旦创建好,默认引用计数器就是1)
  3)当使用alloc、new 或者 copy 创建一个对象时,对象的引用计数器默认就是1


三种内存管理方式:

  MannulReference Counting(MRC,手动管理,在开发 ios4.1之前的版本的项目时我们要自己负责使用引用计数来管理内存,比如要手动retain、release、autorelease 等,而在其后的版本可以使用 ARC ,让系统自己管理内存
  automatic reference counting(ARC,自动引用计数,ios4.1之后推出的);
  garbage collection(垃圾回收).iso不支持垃圾回收;

其中,ARC作为苹果新提供的计数,苹果推荐开发者使用ARC技术来管理内存;

手动管理内存快速入门

1.关闭ARC的方法(其中之一) (OC项目创建完成以后默认的是ARC机制,我们要使用MRC就需要关闭ARC)
  .设置项目信息
  .项目--->bulid setting ->basic levels --->搜索auto --->ARC的选项改为NO
2.判断对象是否需要回收
使用引用计数器 > 0 不需要回收 否则回收
3.重写dealloc方法
  注意:
     dealloc 方法中必须调用[super dealloc];

 4.让引用计数变化
   1)使用 release 可以让引用计数-1;
   2)使用retain 可以让引用计数器+1;
 5.内存管理
   一般来说,对象的引用计数要进行"平衡"
   retain +new = release 次数
例如:
  Person *p =[Person new];
  NSLog(@"p retainCount =%tu:,[p retainCount]);
  [p run];

  [p retain];
//如果想回收,应该让retainCount =0
  [p release];
  NSLog(@"p retainCount =%tu:,[p retainCount]);
  [p release];



重写dealloc 方法,代码规范

1)一定要[super dealloc] ,而且要放到最后,意义是:先释放子类占用的空间在释放父类占用的空间
2)对self(当前)所拥有的其他对象做一次release操作
  -(void)dealloc{
    [_car release];
    [super dealloc];
  }

注意:
  1)永远不要直接通过对象调用dealloc方法(实际上调用并不会出错)
    一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)为了防止调用出错,可以将"野指针"指向nil(0)

  

内存管理的原则

1)原则
  只要还有人在使用某个对象,那么这个对象就不会被回收;
  只要你想使用这个对象,那么就应该让这个对象的引用计数器 +1;
当你不想使用这个对象时,应该让对象的引用计数器为0;
2)谁创建,谁release
  如果你通过alloc new copy 来创建了一个对象,那么你就必须调用release或者autorelease方法
  不是你创建的就不用你去负责
3)谁retain,谁release
  只要你调用了retain,无论这个对象是如何生成的,你都要调用release
4)总结
  有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1

  

内存管理研究内容

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

  

单个对象的野指针问题

例如:
  //如果对象被释放了,此时还能不能再继续使用对象
  Person *p =[Person new];// retainCount =1
  [p run];
  //谁创建谁释放
  [p release];

  //当 p 被release 之后,p就是一个野指针了
  //当p 没有被改变或其他引用时,是可以访问到原来的空间的
  //另:内存释放,是将地址的引用释放出来,电脑的删除事实上都是覆盖操作
  //那么当p成为野指针后,如何让程序报错,不运行呢?可以开启Xcode中的野指针检测
  //在 edit scheme --->Diagnostics--->Objective Enable Zombie Objects 选中就可以了
  [p run];// 能运行吗?有可能会运行
  //不能使用 [p retain]让僵尸对象起死复生

  //另:空指针:没有指向任何东西的指针,给空指针发送信息不会报错
  关于nil 和 Nil 及 NULL的区别

  nil: A null pointer to an Objective-C object.(#define nil((id)0))
  nil 是一个对象值
  Nil: A null pointer to an Objectve-C class.
  NULL : A null pointer to anything else(#define NULL((void*)0))
  NULL 是一个通用指针(泛型指针)
  NSNull :A class defines a singleton object used to represent null values in collection objects(which don‘t allow nil values).
  [NSNull null]:The singleton instance of NSNull
  [NSNull null]是一个对象,它用在不能使用nil的场合

  

避免使用僵尸对象的方法

为了防止不小心调用了僵尸对象,可以将对象赋值nil(对象的空值)
p=nil;//给p对象赋值 赋值空 ,p =nil后 [p retain],相当于[nil retain],不会报错,但是应避免使用僵尸对象

对象的内存泄露

1)retain和release个数不匹配,导致内存泄露

  Car *car =[[Car alloc]init];//1
  [car retain];//2
  [car retain];//3
  [car retain];//4
  
  [car release];//release 让 retainCount-1

  NSLog(@"%ld",car.retainCount);

2)对象使用过程中被赋值了nil,导致内存泄露
  
  Car *car =[[Car alloc]init];//1
  [car retain];//2
  [car retain];//3
  [car retain];//4

  car =nil;//car指向了 空
  
  //释放对象的空间
  [car release]; //[nil release];

3)在方法中不当的使用了retain
  -(void)start:(Car *)car;

  -(void)start:(Car *)car{
    [car retain];
  }

多个对象的内存管理

set方法的写法(3)
1)基本数据类型:直接复制
 -(void) setAge:(int)age{
    _age=age;
  }
2)OC对象类型
-(void)setCar:(Car*) car{
  //判断_car存放的是否是 形参对象,如果不是,则执行[_car realease];
  if(_car !=car ){
    [_car release];//先释放上一个对象,(注意第一次是nil发送release消息)
    _car = [car retain];
  }
}

  

循环retain的问题

ClassA.h:

    #import <Foundation/Foundation.h>
    @class ClassB;
    @interface ClassA : NSObject

    @property(nonatomic,retain) ClassB *b;
    @end

ClassA.m:

    #import "Class A.h"
    #import "ClassB.h"
    @implementation ClassA

    -(void)dealloc{
        NSLog(@"a释放了");
        [_b release];
         [super dealloc];
    }
    @end

ClassB.h:

    #import <Foundation/Foundation.h>
    @class ClassA;
    @interface ClassB : NSObject
    @property(nonatomic,retain) ClassA *a;
    @end

ClassB.m:
    #import "ClassB.h"
    #import "Class A.h"
    @implementation ClassB
    -(void)dealloc{
            NSLog(@"b释放了");
        [_a release];
        [super dealloc];
    }
    @end

main.m:
    #import <Foundation/Foundation.h>
    #import "Class A.h"
    #import "ClassB.h"
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            ClassA *classa =[[ClassA alloc]init];
            ClassB *classb =[[ClassB alloc]init];
        
            classa.b=classb;//加上这两段代码,下面的就释放不掉了
            classb.a=classa;
        
            [classb release]; 
            [classa release];
        
        //循环retain的解决方法 1)
        //让其中一个在释放依次
        [classb release]; //在写一次
        //第二种解决方法
        //@property(nonatomic,retain)的时候让一个对象 使用retain 另外一个对象使用assing
        }
        return 0;
    }

NSString类的内存管理问题

 1 1.多个对象内存泄露问题
 2      1----->NSString *str=[[NSString alloc]initWithString:@"ABC"];
 3      2----->str=@"123";
 4      3----->[str release];
 5      4----->NSLog(@"%@",str);
 6 
 7     首先,咱们先对这段代码进行分析.
 8         第一行: 声明了一个NSString类型的实例 str,并将其初始化init后赋值为@"ABC"
 9     
10          第二行:将str的指针指向了一个常量@"123"。理论上讲在第一行初始化的@"ABC"没有任何指针指向了.所以造成了内存泄露
11          第三行:将str的引用计数-1
12          第四行: 输入str的值 为123
13          首先回答为什么不会崩溃,因为第三行的release 实际上是release了一个常量@"123",而作为常量其默认的引用计数值是很大的(100K+)
14         可尝试下输出:
15             NSLog(@"retainCount = %tu",[@"123" retainCount]);
16           最终的输出值会是一个很大很大的数。所以单单一个release是不会将其释放掉的.
17     然后在回答这样会不会造成内存泄露
18            理论上讲,是会发生的,但是实际上,Objective-C对NSString类型有特殊照顾,所有的NSString的引用计数器默认初始值都会非常大
19            查阅了一下官方的文档,第一句就是"Do not use this method" 后面给出了说明,因为Autorelease pool的存在,对于内存的管理会相当复杂,retainCount就不能用作调试内存时的依据了,这样对于第一段的结果就可以理解了,可能系统对于这一个特殊的对象有特殊的处理(没准framework里面有早就创建了这个对象了),于是我们拿到了一个非常出人意料的结果

2.危险地用法

1 while([a retainCount]>0){
2       [a release];  
3 }

 

OC的内存管理

标签:

原文地址:http://www.cnblogs.com/developer-wang/p/4503081.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!