黑马程序员——OC内存管理

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、内存管理

1.为什么管理

1>移动设备的内存及其有限,因此每个app运行是的内存也进行了限制(限制值不同设备不一样的标准),如果运行占据内存超标,IOS系统会将此app进行强行杀掉。
2>如果app内存接近临界值会发出警告,这会影响用户体验。为了避免这个问题需要对内存进行管理 : 当一些变量,对象不需要使用内存的时候,对他们占据的内存进行及时回收。

2.内存管理范围

1>栈:存放局部变量(所占用内存会自动销毁)
2>堆:存放对象(所占用内存需手动销毁)
3>管理范围:所有继承NSObject的类对象

二、引用计数器

1.作用

用于计算对象被使用的次数,是一个整数(每个对象都有自己的引用计数器:占4个字节) 

2.特点

1>当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1

2>给对象发送一条retain消息,可以使引用计数器值+1(retain方法返回对象本身)

3>给对象发送一条release消息,可以使引用计数器值-1

4>可以给对象发送retainCount消息获得当前的引用计数器值

// Person.h
    #import <Foundation/Foundation.h>
     
    @interface Person : NSObject
     
    @end   
     
    // Person.m
    #import "Person.h"
    @implementation Person
    @end
     
    // main.m
    #import "Person.h"
    int main(){
        /*
        Person *p = [Person new];
        int count = [p retainCount];
        NSLog("count = %d",count);                            // count = 1
        */
         
        Person *p = [[Person alloc] init];
        int count = [p retainCount];
        NSLog("count = %d",count);                            // count = 1
         
        [p retain];
        count = [p retainCount];
        NSLog("count = %d",count);                            // count = 2
         
        [p release];
        count = [p retainCount];
        NSLog("count = %d",count);                            // count = 1
         
        [p release];
        count = [p retainCount];
        NSLog("count = %d",count);                            // count = 0
         
        p = nil;
        return 0;
    }

3.对象被销毁

1>引用计数器为0,对象占用的内存被系统收回。(对象变成僵尸对象。指向此方法的指针值变为0)
2>当对象被销毁时,系统会自动向对象发送dealloc消息。(对象遗言)
3>一般要重写dealloc方法。(必须调用super的dealloc)
            - (void)dealloc
            {
             [super dealloc];
            }

#import <Foundation/Foundation.h>
     
    @interface Person : NSObject
     
    @property int age;
     
    @end   
     
    // Person.m
    #import "Person.h"
    @implementation Person
     
    - (void) dealloc
    {
        NSLog(@"调用 dealloc 方法");
        /*
            可以在这方法内做一些回收成员变量(如果是OC对象)的操作。
        */
        [super dealloc];
    }
    @end
     
    // main.m
    #import "Person.h"
    int main(){
        Person *p = [[Person alloc] init];
        int count = [p retainCount];
        NSLog("count = %d",count);                            // count = 1
         
        [p release];                                          // 调用 dealloc 方法
        count = [p retainCount];
        NSLog("count = %d",count);                            // count = 0
         
        // 操作僵尸对象
        // [p setAge:10];                                     // message sent to deallocated instance 
        p = nil;                                              // 当对象内存被回收时候,把指针置为空防止野指针出现
        return 0;

4.野指针(僵尸对象

1>如果一个指针指向了一块被回收的内存,那么这个指针成为野指针,一个指针指向一块垃圾内存,如果使用指针来操作被回收的内存后果非常严重 ,被释放内存的OC对象叫僵尸对象。

2>当你用了一个指向不可预知的指针时,即使程序运行没有问题, 那也是非常危险的. 因为这个”野指针”指向的内存空间,可能是某个重要的数据或其它程序,甚至是系统的重要内存位置. 这样造成的危害是不可预知的,这个不可预知包括危害程度的不可预知和危害时间的不可预知的. 像一颗不知何时会爆的定时炸弹。

三、内存管理原则

1>当想使用(占用)某个对象时,就应该让对象的计数器+1 (让对象做一个ratain操作)
2>当不想使用(占用)某个对象时,就应该让对象的计数器-1 (让对象做一次release操作)
3>谁retain,谁release;谁alloc,谁release。
注:对象在set方法中赋值时,应该进行一次retain操作。(基本数据类型不需要管理内存 )
// Person.h
    #import <Foundation/Foundation.h>
     
    @interface Person : NSObject
     
    @property int age;
     
    @end   
     
    // Person.m
    #import "Person.h"
    @implementation Person
     
    - (void) dealloc
    {
        NSLog(@"调用 dealloc 方法");
        /*
            可以在这方法内做一些回收成员变量(如果是OC对象)的操作。
        */
        [super dealloc];
    }
    @end
     
    // main.m
    #import "Person.h"
    int main(){
        Person *p = [[Person alloc] init];
        int count = [p retainCount];
        NSLog("count = %d",count);                            // count = 1
         
        [p release];                                          // 调用 dealloc 方法
        count = [p retainCount];
        NSLog("count = %d",count);                            // count = 0
         
        // 操作僵尸对象
        // [p setAge:10];                                     // message sent to deallocated instance 
        p = nil;                                              // 当对象内存被回收时候,把指针置为空防止野指针出现
        return 0;
    }

四、set方法内存管理

1.只要调用了alloc,则必须要有release(或autorelease);
            如果对象不是通过alloc产生,则不需要release。
2.set方法代码规范
      1>基本数据类型:直接赋值(基本数据类型不需要进行内存管理)
- (void)setAge:(int)age  
       {  
         _age = age;   
         }  
2> OC对象类型
-  (void)setCar:(Car *)car  
{  
   if ( car != _car) // 1.先判断是不是新传进来的对象  
{  
   [_car release]; // 2.对旧对象做一次release  
    _car = [car retain]; //  3.对新对象做一次retain  
}                               
}  
3> dealloc代码规范
         a.一定要有[super dealloc],而且要放到最后。
         b.对self(当前)所拥有的其他对象做一次release。
- (void)dealloc  
     {  
      [_car release]; // 一旦Person release,则车也应该release  
      [super dealloc]; // 这段一定要放最后  
     }  

五、循环引用

1.使用场景

1>在使用循环引用场景中不能简单引用别的类的头文件,这样会导致编译找不到相应类的定义。解决方法是将该类声明为 @class。
2>作用是 : 就可以引用一个类,说明一下它是一个类
3>@class和#import的区别
a.#import方式会包含被引用类的所有信息,包括被引用类的变量和方法;@class方式只是告诉编译器在A.h文件中 B *b 只是类的声明,具体这个类里有什么信息,这里不需要知道,等实现文件中真正要用到时,才会真正去查看B类中信息
b.如果有上百个头文件都#import了同一个文件,或者这些文件依次被#improt,那么一旦最开始的头文件稍有改动,后面引用到这个文件的所有类都需要重新编译一遍,这样的效率也是可想而知的,而相对来 讲,使用@class方式就不会出现这种问题了
c.在.m实现文件中,如果需要引用到被引用类的实体变量或者方法时,还需要使用#import方式引入被引用类。

2.循环引用的解决方案

一端用retain,一端用assign。
#import <Foundation/Foundation.h>                                                        
    @class B
     
    @interface A : NSObject
     
    @property (noatomic,assign) B *b;                                                    // 对象 B 使用 assign 策略
     
    @end
 /*--------------------------------------- A.m ---------------------------------------*/
     #import "A.h"
     #import "B.h"
      
     @implementation A
      
     - (void) dealloc
     {
         NSLog(@"调用了对象 A 的 dealloc 方法");
         // [_b release];                                                              // 使用 assign 无需release
         [super dealloc];
     }
     @end
      
/*--------------------------------------- B.h ---------------------------------------*/
    #import <Foundation/Foundation.h>                                                   
    @class A
     
    @interface B : NSObject
     
    @property (noatomic,retain) A *a;                                                    // 对象 A 使用 retain 策略
     
    @end
 /*--------------------------------------- B.m ---------------------------------------*/
     #import "A.h"
     #import "B.h"
      
     @implementation B
      
     - (void) dealloc
     {
         NSLog(@"调用了对象 B 的 dealloc 方法");
         [_a release];
         [super dealloc];
     }
     @end
 
 /*--------------------------------------- main.m ---------------------------------------*/
     #import "A.h"
     #import "B.h"
      
     int main(){
         A *a = [[A alloc] init];                                                       // a 的计数器为 1
         B *b = [[B alloc] init];                                                       // b 的计数器为 1
          
         [a setB:b];                                                                    // 由于使用 assign 策略 b  的计数器为 1
         [b setA:a];                                                                    // 由于使用 retain 策略 a  的计数器为 1 + 1 = 2    
          
         [a release];                                                                   // a 的计数器为 2 - 1 = 1
         [b release];                                                                   // b 的计数器为 1 - 1 = 0 
                                                                                            // b 对象回收调用 dealloc 方法 同时 release a
                                                                                            // 输出结果 : 
                                                                                            // 调用了对象 B 的 dealloc 方法 
                                                                                            // 调用了对象 A 的 dealloc 方法  
         return 0;
     }

六、autorelease 

1.作用

1>autorelease会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里所有对象做一次release。
2>返回对象本身,且不影响对象计数器。

2.特点

1>给某个对象发送一条autorelease消息时,就会将这个对象加到一个自动释放池中

2>当自动释放池销毁时,会给池子里面的所有对象发送一条release消息

3>调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身

4>autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autorelease pool中,当该pool被释放时,该pool中的所有对象会被调用Release

3.简单的使用

<span style="font-size:12px;">#import <Foundation/Foundation.h>
 
    @interface Person : NSObject
     
    @end   
     
    /*--------------------------------------- Person.m ---------------------------------------*/
    #import "Person.h"
    @implementation Person
     
    - (void) dealloc
    {
        NSLog(@"调用 dealloc 方法");
        [super dealloc];
    }
    @end
     
    /*--------------------------------------- main.m ---------------------------------------*/
    #import "Person.h"
     
    int main(){
        @autoreleasepool{
            Person *p = [[[Person alloc] init] autorelease];                                        
        }               // 输出结果 : 调用 dealloc 方法
         
         
         
        return 0;
    }</span>
注意:

1>ios 5.0后

@autoreleasepool

 {

 // ....

}

2>ios 5.0前

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

// .....

 [pool release]; // 或[pool drain];

七、总结与心得

1.在内存管理中如果有alloc、new或者copy,就要有一条retain消息。
2.谁调用谁就释放。一是为了阅读方便,二属于内存管理的原则。
3.内存管理是很复杂的,这块需要多加练习,写的多了时候,往往就会忽略内存管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值