内存管理和引用计数

ARC

在LLVM编译器中设置ARC为有效状态,就无需再次输入retain或者release代码

照明对比引用计数 对照明设备所做的动作 对oc对象所做的动作 引用技数 oc方法
第一个进入办公室的人 开灯 生成并持有对象 0 --- >1 alloc,new,copy,mutableCopy等方法
之后每当有人进入办公室 需要照明 持有对象 1 --- >2 retain方法
每当有人下班离开办公室 不需要照明 释放对象 2 --- > 1 release方法
最后一个人下班离开办公室 关灯 废弃对象 1 ----> 0 dealloc方法

alloc,retain,release,dealloc的实现总结,
1, 在oc的对象中存有应用计数这一整数值
2, 调用allocretain方法后,引用计数值加1
3, 调用release后,引用计数值减1
4, 引用计数值为0时,调用dealloc方法废弃对象

内存管理的思考方式

  • 自己生成的对象,自己所持有

    • 自己生成并持有对象的源代码

      /*  

      使用 NSObject类的alloc类方法就能自己生成并持有对象,指向生成并持有对象的指针被赋给变量obj,[NSObject new]与[[NSObject alloc]init]是完全一致的
    */
    id obj = [[NSObject alloc] init];
    id obj = [NSObject new];
  • 使用以下方法也可以生产并持有对象
alloc new copy mutableCopy

copy方法利用基于NSCopying方法约定,由各类实现的copyWithZone:方法生成并持有对象的副本.
mutableCopy方法利用基于NSMutableCopying方法约定,由各类实现的mutableCopyWithZone:方法生成并持有对象的副本.
两者的区别在于,copy方法生成不可变更的对象,而mutableCopy方法生产可变更的对象

  • 使用以下名称开头的方法名,也意味着自己生成并持有对象

    allocMyObject newThatObject copyThis mutableCopyYourObject

  • 非自己生成的对象,自己也能持有

    • 通过retain方法,非自己生成的对象跟用alloc ,new,copy,mutableCopy方法生成并持有的对象一样,成为了自己所持有的.

    • 源码展示



    /*
     NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有该对象,但retain方法可以持有对象
     */
    
    id obj = [NSMutableArray array];
    [obj retain];


    ```

    

- 不再需要自己持有的对象时要释放

    - 自己持有的对象,一旦不再需要,持有者有义务释放该对象.释放使用``release``方法

        - 源码展示

```objc

          // 用alloc方法由自己生成并持有的对象就通过release方法释放了

    /* 自己生成并持有对象*/
    id obj = [[NSObject alloc] init];
    /* 释放对象,*/
    [obj release];
        


        ```

    
- 取得非自己生产并持有的对象

   - 源码展示

``` objc

        /*取得的对象存在, 自己不持有对象*/

    id obj = [NSMutableArray array];
    /* 自己持有对象*/
    [obj retain];
    /* 释放对象,对象不可再被访问*/
    [obj release];
     ```
    
- 用某个方法生成对象,并将其返还给该方法的调用方
    > 原封不动的返回用``alloc``方法生成并持有的对象,就能让调用方也持有该对象,  ``allocObject``是符合驼峰命名规范的,即将第一个词后每个词的首字母大写来拼写复合词的记法. 外部调用,``id obj1 = [obj0 allocObject];``,即自己持有对象  

    - 源码展示

``` objc

    - (id)allocObject{

        
        /* 自己生成并持有对象*/


        id obj = [[NSObject alloc] init];


        /* 自己持有对象*/
       return obj;

    }
    ```
 - 调用``[NSMutableArray array]`` 方法使取得的对象存在,但自己不持有对象
   
  > 使用autorelease方法,可以使取得的对象存在,但自己不持有对象,``autorelease``提供这样的功能,使对象在超出置顶的生存范围时能够自动并正确地释放(调用``release``方法)

- 源码展示

    ```objc

    - (id)object
    {
        /* 自己生成并持有对象*/
        id obj = [[NSObject alloc] init];
        /* 自己持有对象*/
        [obj autorelease];
        /* 取得的对象存在,但自己不持有对象 */
        return obj;
    }
  • 外部调用
// 取得的对象存在,但自己不持有对象
id obj1 = [obj0 object];
// 通过retain方法将调用autorelease方法取得的对象变为自己持有
[obj1 retain];
    ```
  • 非自己持有的对象无法释放

自己生成并持有对象后,在释放完不再需要的对象之后再次释放


    /* 自己生成并持有对象*/


    id obj = [[NSObject alloc]init];


    /* 对象已释放*/


    [obj release];


    /* 释放之后再次释放已非自己持有的对象,应用程序崩溃!


        崩溃情况: 再度废弃已经废弃了的对象时崩溃,访问已经废弃的对象时崩溃


     */


    [obj release];
  
    ```
     
   > 取得的对象存在,但自己不持有对象时释放
 
```objc


    /*取得的对象存在,但自己不持有对象*/
    id obj1 == [obj0 object];
    /* 释放了非自己持有的对象,肯定会导致应用程序崩溃*/
    [obj1 release];
    ```


# ``alloc``,``retain``,``release``,``dealloc``实现

    

> CGUstep是Cocoa框架的互换框架,从使用者的角度来看,两者的行为和实现凡是是一样的



- ``GNUstep``源代码中``NSObject``类的类方法实现

    - ``+ (id)alloc``通过``+(id)allocWithZone:(NSZone * )z``调用``NSAllocateObject``函数分配了对象,``NSAllocateObject``函数又通过调用``NSZoneMalloc``函数来分配存放对象所需的内存空间,之后将该内存空间置为0,最后返回作为对象而使用的指针

    - ``alloc``类方法用``struct obj_layout``中的``retained``整数来保存引用计数,并将其写入对象内存头部,该对象内存块全部置为0后返回

    - 对象的引用计数可通过``retainCount``实例方法取得,内部调用``NSExtraRefCount``函数,执行``alloc``后对象的``retainCount``是``1``.

    - 由对象寻找到对象内存头部,从而访问其中的``retained``变量,因为分配时全部置为0,所以``retained``为0,由``retainCount``方法内部的``NSExtraRefCount(self)+1``得出,``retainCount`为1.即``retain``方法使``retained``变量加1,而``release``方法使``retained``变量减1.



- 苹果的实现

    - ``alloc``类方法首先调用``allocWithZone:``类方法,然后调用``class_createInstance``函数,最后通过`calloc``来分配内存块

    - ``retainCount``,``retain``,``release``各方法都调用了``_CFDoExternRefOperation``函数,苹果的实现大概就是采用散列表(引用计数表)来管理引用计数



-  GNUstep和苹果实现进行对比

    - ``GNUstep``将引用计数保存在对象占用内存块头部的变量中,而苹果则是保存在引用技术表的记录中

    - 内存块管理的好处:

        > 1.少量的代码即可完成 

            2.能过统一管理引用计数用内存块与对象用内存块

    - 通过引用技术表管理引用计数的好处:

       > 1. 对象用内存块的分配无需考虑内存块头部 

        2.引用计数表各记录中存有内存块,可从各个记录追溯到各对象的内存块.

# autorelease使用

 > ``autorelease``自动释放,类似于C语音中局部变量的特性(程序执行时,若某自动变量超出其作用域,该自动变量将被自动废弃),``autorelease``会在对象实例超出其作用域时,调用对象实例的``release``方法.



 - ``autorelease``的具体使用方法如下:

    > 1.生成并持有``NSAutoreleasePool``对象

        2.调用已分配对象的``autorelease``实例方法

        3.废弃``NSAutoreleasePool``对象,将调用``release``方法



# ``autorelease``实现

   - ``GNUstep``中实现

   > ``autorelease``实例方法的本质就是调用``NSAutoreleasePool``对象的``addObject``类方法.

   - 苹果的实现

    > 1.``static inline void *push()`` 相当于生成或持有``NSAutoreleasePool``类对象

        2.``static inline void *pop(void *token)``内部为``releaseAll()``方法, 相当于废弃``NSAutoreleasePool``类对象

    3.``static inline id autorelease(id obj)``内部调用``add(obj)``方法(将对象追加到内部数组中),相当于``NSAutoreleasePool``类的``addobject``类方法



 - 相关问题

    > 如果NSAutoreleasePool对象调用 autorelease会如何?
  
  ```objc
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [pool release];

会发生异常,在使用oc的Foundation框架时,无论使用哪一个对象的autorelease实例方法,实际上都是调用NSObject类的autorelease实例方法.但是对于NSAutoreleasePool类,autorelease实例方法已被该类重载,因此运行时就会出错

你可能感兴趣的:(内存管理和引用计数)