Object-C——内存管理

一、计数器

   (一)、引用计数器的基本操作

        1、 方法的基本使用
          ①  retain :计数器+1,会返回对象本身
          ②  release :计数器-1,没有返回值
          ③  retainCount :获取当前的计数器
          ④ dealloc
             * 当一个对象要被回收的时候,就会调用
             * 一定要调用[super dealloc],这句调用要放在最后面

 1 #import "Person.h"
 2 
 3 @implementation Person
 4 
 5 
 6 // 当一个Person对象被回收的时候,就会自动调用这个方法
 7 - (void)dealloc
 8 {
 9     NSLog(@"Person对象被回收");
10     
11     // super的dealloc一定要调用,而且放在最后面
12     [super dealloc];
13 }
14 
15 @end


        2、 概念
         ①  僵尸对象 :所占用内存已经被回收的对象,僵尸对象不能再使用
         ②  野指针 :指向僵尸对象(不可用内存)的指针,给野指针发送消息会报错(EXC_BAD_ACCESS)
         ③  空指针 :没有指向任何东西的指针(存储的东西是nil、NULL、0),给空指针发送消息不会报错

          3、代码举例

 1 #import <Foundation/Foundation.h>
 2 #import "Person.h"
 3 
 4 int main()
 5 {
 6     // 1
 7     Person *p = [[Person alloc] init];
 8     
 9     //NSUInteger c = [p retainCount];
10     
11     //NSLog(@"计数器:%ld", c);
12     
13     
14     // 2 retain方法返回的是对象本身
15     [p retain];
16     
17     // 1
18     [p release];
19     
20     // 0 野指针:指向僵尸对象(不可用内存)的指针
21     [p release];
22     
23     [p retain];
24     
25     // -[Person setAge:]: message sent to deallocated instance 0x100109a10
26     // 给已经释放的对象发送了一条-setAge:消息:
27     p.age = 10;
28     //[p setAge:10];
29     
30     // 指针p变成空指针
31     //p = nil;
32     
33     // EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存
34     // 野指针错误
35     // OC不存在空指针错误,给空指针发送消息,不报错
36     [p release];
37     [p release];
38     [p release];
39     [p release];
40     
41     [nil release];
42     
43     return 0;
44 }

(二) 多个对象之间的内存管理

       1、 你想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
       2、 你不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
       3、 谁retain,谁release
       4、 谁alloc,谁release

 1 // b-1
 2     Book *b = [[Book alloc] init];
 3     // p-1
 4     Person *p1 = [[Person alloc] init];
 5     
 6     //p1想占用b这本书
 7     // b-2
 8     [p1 setBook:b];
 9     
10     // p-0
11     // b-1
12     [p1 release];
13     p1 = nil;
14     
15     // b-0
16     [b release];
17     b = nil;

 (三)、set方法的内存管理

    1、内存管理代码规范:
        ①  只要调用了alloc,必须有release(autorelease)
        ②  对象不是通过alloc产生的,就不需要release

    2、set方法

        ① 代码规范实现

 1 //基本数据类型:直接复制
 2  - (void)setAge:(int)age
 3  { 
 4     _age = age;
 5  }
 6  
 7 // OC对象类型
 8  - (void)setCar:(Car *)car
 9  {
10     // 1.先判断是不是新传进来对象
11     if ( car != _car )
12     {
13         // 2.对旧对象做一次release
14         [_car release];
15  
16         // 3.对新对象做一次retain
17         _car = [car retain];
18     }
19  }

     3、dealloc方法的代码规范实现

1 //一定要[super dealloc],而且放到最后面
2 // 对self(当前)所拥有的其他对象做一次release
3  - (void)dealloc
4  {
5     [_car release];
6     [super dealloc];
7  }

  (四) @property参数

       1、 控制set方法的内存管理

            *retain : release旧值,retain新值(用于OC对象)

// retain : 生成的set方法里面,release旧值,retain新值
@property (retain) Book *book;
@property (retain) NSString *name;

           *assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)

@property ( assign) int height;

            *copy   : release旧值,copy新值(一般用于NSString *)

        2、控制生成或者不生成set方法

             *readwrite :同时生成set方法和get方法(默认)

             *readonly  :只会生成get方法

@property (readwrite, assign) int height;

         3、多线程管理

             *atomic    :性能低(默认)

             *nonatomic :性能高

@property (nonatomic, assign, readwrite) int weight;

          4、控制set方法和get方法的名称

              *setter : 设置set方法的名称,一定有个冒号:

              *getter : 设置get方法的名称

// 返回BOOL类型的方法名一般以is开头
@property (getter = isRich) BOOL rich;

  (五) 循环引用

       1、 @class

          ①  作用:仅仅告诉编译器,某个名称是一个类
          ②  开发中引用一个类的规范
            *在.h文件中用@class来声明类

 1 #import <Foundation/Foundation.h>
 2 #import "Card.h"
 3 // @class仅仅是告诉编译器,Card是一个类
 4 //@class Card;
 5 
 6 @interface Person : NSObject
 7 
 8 @property (nonatomic, retain) Card *card;
 9 
10 @end


            * 在.m文件中用#import来包含类的所有东西

 1 #import "Person.h"
 2 #import "Card.h"
 3 
 4 @implementation Person
 5 
 6 - (void)dealloc
 7 {
 8     NSLog(@"Person被销毁了");
 9     [_card release];
10     
11     [super dealloc];
12 }
13 
14 @end

    2、循环retain
       ①问题

          *A对象retain了B对象,B对象retain了A对象

          *这样A、B对象永远无法释放

       ②两端循环引用解决方案
         * 一端用retain
         * 一端用assign          


  (六)、 autorelease

           1、autorelease的基本用法
              ①  会将对象放到一个自动释放池中
              ②  当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
              ③  会返回对象本身
              ④  调用完autorelease方法后,对象的计数器不变
 
            2、autorelease的好处
              ①  不用再关心对象释放的时间
              ②  不用再关心什么时候调用release
 
           3、autorelease的使用注意
              ①  占用内存较大的对象不要随便使用autorelease
              ②  占用内存较小的对象使用autorelease,没有太大影响
           4、错误写法
              ①  alloc之后调用了autorelease,又调用release

1  @autoreleasepool
2  {
3     // 1
4     Person *p = [[[Person alloc] init] autorelease];
5  
6     // 0
7     [p release];
8  }
9  

           ②  连续调用多次autorelease

1 @autoreleasepool
2  {
3     Person *p = [[[[Person alloc] init] autorelease] autorelease];
4  }

          5、自动释放池
           ①  在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)

           ②  当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池

           6、自动释放池的创建方式

@autoreleasepool
 {
    
 }

          7、代码举例

 1 void test()
 2 {
 3     @autoreleasepool
 4     {// { 开始代表创建了释放池
 5         
 6         // autorelease方法会返回对象本身
 7         // 调用完autorelease方法后,对象的计数器不变
 8         // autorelease会将对象放到一个自动释放池中
 9         // 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
10         Person *p = [[[Person alloc] init] autorelease];
11         
12         p.age = 10;
13         
14         
15         
16         @autoreleasepool
17         {
18             // 1
19             Person *p2 = [[[Person alloc] init] autorelease];
20             p2.age = 10;
21             
22             
23         }
24         
25         
26         Person *p3 = [[[Person alloc] init] autorelease];
27         
28         
29     } // } 结束代表销毁释放池
30 }

  

原文地址:https://www.cnblogs.com/gaizuojia/p/4357563.html