首页 > 代码库 > Objective-C:07_内存管理

Objective-C:07_内存管理

基本原理:

    1、什么是内存管理
        -》移动设备的内存极其有限,每个app所能占用的内存是有限制的
        -》当app所占用的内存比较多时,系统会发出内存警告,这时得回收一些不需要在使用的内存空间,比如回收一些不需要使用的对象、变量等
        -》管理范围:任何继承了NSObject的对象。对其他基本数据类型(int、char、float、double、struct、enum等)无效
    
    局部变量都放在了内存中的栈空间中,对象放在堆空间中
    堆空间中的东西是动态分配的,需要我们手动回收。栈空间不用我们自己管
 
 
    2、对象的基本结构
        每个OC对象都有自己的引用计数器,是一个整数,表示"对象被引用的次数",即有多少人正在使用这个OC对象。(分配4个字节的存储空间存放引用计数器)。当对象刚刚产生的时候引用计数器是1
    
    3、引用计数器的作用
        当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认是1    
        当一个对象的引用计数器值为0是,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出。
    
    4、引用计数器的操作
        -》给对象发送一条retain消息,可以使引用计数器值 +1 (retain方法返回对象本身)
        -》给对象发送一条release消息,可以使引用计数器值 -1 (没有返回值)
        -》可以给对象发送 retainCount消息,获得当前的引用计数器值
 
        关闭ARC(Automatic Reference Counting):
            选中项目名称→Building Settings→Levels→Apple LLVM 6.0 -Language -Objective C将下面的YES改为no
        
        NSUInteger:相当于long,输出的时候占位符使用%ld
        
    僵尸对象:引用计数器变成0的对象,将不能使用,称为僵尸对象
    野指针:指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错
    空指针:没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错
    错误:EXC_BAD_ACCESS:访问了一块坏的内存(已经被回收、已经不可用的内存);
    
    为了防止访问坏内存的错误,我们可以在对象的计数器的值减为0之后,将指针赋空值
    p=nil;//将指针p变成空指针
    OC中不存在空指针错误,给空指针发送消息,不会报错
    
    -[Person setAge:]: message sent to deallocated instance 0x100202230
        给已经释放的对象发送了一条setAge:消息
 
    5、对象的销毁
        -》当一个对象的引用计数器值为0时,那么他将被销毁,其占用的内存被系统回收
        -》当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
        -》一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
        -》一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
        -》不要直接调用dealloc方法
        -》一旦对象被回收,它占用的内存就不能再用,坚持使用会导致程序崩溃(野指针错误)
 
 
内存管理原则 
    对对象进行一次retain,那么使用结束的时候就要release
    谁创建,谁release。如果你通过alloc、new创建一个对象,那么你必须调用release
    谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release
    
    你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
    你不想再使用(占用)某个对象,就应该让对象的设计器-1(让对象做一次release)
    
    在类的实现中获取成员变量值方法:
        _speed:直接访问成员变量
        self->_speed:直接访问成员变量
        self.speed:get方法
        [self speed]:get方法
    
    内存管理代码规范:
        1、只要调用了alloc,必须有release(aotorelease)
               如果对象不是通过alloc产生的,就不需要release
        2、set方法的代码规范
                -》基本数据类型:直接赋值
                    - (void)setAge:(int)age
                    {
                        _age=age;
                    }
                -》OC对象类型
                    - (void)setCar:(Car *)car
                    {
                        //1、判断是不是新传进来的对象
                        if(car != _car)
                        {
                            //2、对旧对象做一次release
                            [_car release];
                            //3、对新对象做一次retain
                            _car = [car retain];
                        }
                    }
 
        3、dealloc方法的代码规范
               -》一定要[super dealloc],而且要放到最后面
               -》对self(当前)所拥有的其他对象做一次release
            - (void)dealloc
            {
                [_car release];
                [super dealloc];
            }
 
@property的内存管理
    @property (retain) Book *book;
    生成的set方法如下:
          - (void)setBook:(Book *)book
{
    if(_book != book)
    {
        [_book release];
        _book = [book retain];
    }
}
 
    1、set方法内存管理相关参数:
       retain :release旧值,retain新值(适用于OC对象类型)
       assign:直接赋值(默认,适用于非OC对象类型)
       copy   :release旧值,copy新值
 
    2、是否要生成set方法
        readwrite:同时生成setter和getter的声明、实现(默认)
        readonly :只会生成getter的声明、实现
 
    3、多线程管理
        nonatomic:生成set方法的时候不要加多线程代码。性能高(一般就用这个)
        atomic       :生成set方法的时候加多线程代码。性能低(默认)
 
    4、setter和getter方法的名称    
            setter:决定了set方法的名称,一定要有一个冒号(不常用)
            getter:决定了get方法的名称  (一般用在BOOL类型)      
        @property int age;生成的方法名为:age,setAge:
        @property (getter=abc , setter=setAbc: ) int age;
            生成的方法名:get方法:abc
                                    set方法:setAbc:
         
          long表示时间类型的时候,是从1970-01-01 00:00:00开始,一共过了多少毫秒
       
        
 
循环引用:
    涉及到循环引用(你中有我,我中有你),添加引用的时候使用@class  类名
 
    1、@class的作用:仅仅告诉编译器,某个名称是一个类
             @class Card;//仅仅是告诉编译器,Card是一个类
 
    2、开发中引用一个类的规范
        -》在 .h 文件中用@class来声明类
        -》在 .m 文件中用#import来包含类的所有东西
        使用@class可以提高编译器编译效率(当一个类被多个类引用的时候,如果类发生变化,那么需要重新拷贝)
        
    3、两端循环引用解决方案:
        -》一端用retain
        -》一端用assign
 
 
autorelease:半自动释放
    
    @autoreleasepool
    {//{ 开始代表创建了释放池
        Person *p = [[[Person alloc] init] autorelease];
        p.age = 19;
    }// }结束代表销毁释放池
 
    注意:@autorelease是可以嵌套的,是以栈(先进后出)的方式存储的
 
    1、autorelease的基本用法
        -》会返回对象本身
        -》会将对象放到一个自动释放池中
        -》当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
        -》Person *p = [[[Person alloc] init] autorelease]; 调用完autorelease方法后,对象的计数器不变
    
    2、autorelease的好处
        -》不用在关心对象释放的时间
        -》不用再关系什么时候调用release
 
    3、autorelease的使用注意
        -》占用内存较大的对象不要随便使用autorelease
        -》占用内存较小的对象使用autorelease,没有太大影响
 
    4、错误写法
        -》alloc之后调用autorelease,有又调用release    
              @autoreleasepool
    {
        Person *p = [[[Person alloc] init] autorelease];
        [p release];
    }
-》连续调用多次autorelease
    @autoreleasepool
    {
        Person *p = [[[[Person alloc] init] autorelease] autorelease];
    }
    
    5、自动释放池
        -》在IOS程序运行过程中,会创建无数个池子。这些池子都是以栈结果存在(先进后出)
        -》当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
        
    6、自动释放池的创建方式
        -》IOS5.0前
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    Person *pp = [[[Person alloc] init] autorelease];
    [pool release];
        -》ios5.0开始
             @autoreleasepool
    {
        Person *p = [[[[Person alloc] init] autorelease] autorelease];
    }
 
 
    1、系统自带的方法里没有包含alloc、new、copy,说明返回的对象都是autorelease的
        NSString *str = @"13423";这种方式创建出来的字符串对象,默认就是autorelease的。不需要我们去管理内存
    2、开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象
        -》创建对象时不要直接使用类名,一般用self
          + (id)person
{
    return [[[self alloc] init] autorelease];
}
 
总结:
    一、计数器的基本操作
            retain:+1
            release:-1
            retainCount:获得计数器        
 
    二、set方法的内存管理
        -》set方法的实现
        - (void)setCar:(Car *)car
        {
            if(_car != car)
            {
                [_car release];
                _car = [car retain];
            }
        }
    
        -》dealloc方法的实现
        - (void)dealloc
        {
            [_car release];
            [super dealloc];
        }
 
    三、@property参数
            -》OC对象
            @property (nonatomic, retain) 类名 *属性名;
            @property (nonatomic, retain) Car *car;
            @property (nonatomic,retain) id car;
            被retain过的属性,必须在dealloc方法中release属性
            
            -》非OC对象
            @property (nonatomic,assign) 类型名称 属性名;
            @property (nonatomic,assign) int age;
 
    四、autorelease
        -》系统自带的方法中,如果不包含alloc、new、copy,那么这些方法返回的对象都已经autorelease过的
            [NSString stringWithForm:.........]
            [NSDate date];
 
        -》开发中经常写一些类方法快速创建一个autorelease的对象
            创建对象的时候不要直接使用类名,使用self
 

Objective-C:07_内存管理