OC---内存管理(在对象属性的- setter和- dealloc方法里面写内存管理代码)

 

内存管理(在对象属性的- setter- dealloc方法里面写内存管理代码)

 

内存管理范围:

任何继承自NSObject的对象;其他数据类型(int、char、double、float、struct、enum等)不需要内存管理

 

对象的引用计数器

每个OC对象内部都有自己的int类型(占据4个字节)引用计数器,表示“对象被引用的次数”。

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

2> 只有当对象的引用计数器为0时,对象占用的堆内存空间才会被系统回收。

 

引用计数器的操作:

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

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

3> 给对象发送一条retainCount消息可以获取当前对象的引用计数器值

 

对象的销毁:   - dealloc方法(不要自己调用)

1> 当一个对象的引用计数器值为0时,对象就会被销毁,其占用的堆内存空间会被系统回收

2> 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息(即系统会自动调用对象的- dealloc方法,- dealloc方法相当于对象的遗言

3> 一般会重写- dealloc方法,在重写的- dealloc方法里释放相关资源。- dealloc方法相当于对象的遗言

4> 重写- dealloc时,必须要在最后调用[super dealloc];

 

 

野指针和空指针

野指针:

指向僵尸对象(指向已经被回收的,不可用的内存)的指针

野指针错误:// EXC_BAD_ACCESS 野指针错误,访问了一块坏内存(已经被回收、已经不可用的内存)

空指针:

OC中不存在空指针错误

[nil release];    // OC中不存在空指针错误,给空指针发送消息,不报错,相当于代码[nil  release];无效  

 

小结:

1.方法的基本使用:

 1> retain :计数器+1,返回对象本身

 2> release : 计数器-1,没返回值

 3> retainCount : 获取当前计数器

 4> dealloc 

 * 当一个对象计数器为0,被回收销毁的时候,系统自动调用

 * 重写dealloc方法一定要在最后调用[super  dealloc];

2.概念:

 1> 僵尸对象 : 所占用内存已经被回收的对象,不能再使用

 2> 野指针 : 指向僵尸对象(不可用内存)的指针。给野指针发送消息报错:EXC_BAD_ACCESS

 3> 空指针 : 没有指向任何东西的指针(存储的东西是nil、NULL、0)。给空指针发送消息不会报错,无效

 

 

内存管理原则:

 1>

  * 只要还有人在用某个对象,那么这个对象就不会被回收

  * 只要你想用这个对象,就要让这个对象的计数器+1

  * 当你不再使用这个对象,就让这个对象的计数器-1

 

 2> 谁创建,谁release

 * 如果你通过alloc、new或者(mutable)copy来创建一个对象,就必须调用该对象的release或autorelease

 * 也就是说:如果这个对象不是你创建的,就不用你去release或者autorelease

 

 3> 谁retain,谁release

 * 只有你调用了对象的retain,最后你都要调用对象的release

 

 

总结:

 1> 你想使用(占有)某个对象,就应该让该对象的计数器+1(让该对象做一次retain操作)

 2> 你不想再使用(占有)某个对象,就应该让对象的计数器-1(让该对象做一次release操作) 

   3> 谁retain,谁release———alloc,谁release

 

/*

 内存管理代码规范:

 1.只要调用了alloc,必须有releaseautorelease

   换言之:如果对象不是通过alloc产生的,没有调用alloc,就不能有releaseautorelease

 2.set方法的代码规范:

  1> 基本数据类型:直接赋值

    - (void)setAge:(int)age

    {

    // 基本数据类型不需要管理内存

    _age = age;

    }

  2> OC对象类型

     - (void)setBook:(Book *)book

     {

         if (_book != book)    // 1.先判断传进来的是否为新对象

         {

             // 2.对旧对象(当前使用的对象)进行一次release操作

             [_book release];

             // 3.再对新对象做一次retain操作

             _book = [book retain];    // 你想使用(占有)某个对象,就应该让该对象的计数器+1(让该对象做一次retain操作)

         }

     //  如果传进来的对象为原来的旧对象(_book == book)   那么什么都不做

     }

 

  3.dealloc方法的代码规范:

   1> self(当前对象)所拥有的其他对象做一次release

   2> 一定要在最后调用[super dealloc];

     - (void)dealloc

     {

         [_book release];

         [super dealloc];

     }

 

 */

 

 

 

内存管理---@property参数

// @property参数retain  在自动生成的set方法里,会release旧值,retain新值

// @property参数只会影响setter和getter。

@property (nonatomicretain) Book *book;

 

@property (nonatomicretain) NSString *name;

 

 

// 被retain过的属性,必须在dealloc方法中release

// dealloc方法还是要手动重写

- (void)dealloc

{

    [_book release];

    [_name release];

    [super dealloc];

 

}

 

总结:

/* @property参数分类

 1. set方法内存管理相关参数

   * retain release旧值,retain新值(适用于OC对象类型)

   * assign(默认) 直接赋值(默认,适用于非OC对象类型,比如基本数据类型)

   * copy   release旧值,copy新值(适用于OC对象类型)

 

 2. 是否生成set方法

   * readwrite(默认) 同时生成settergetter的声明、实现

   * readonly  只会生成getter的声明、实现;setter不会

 

 3. 多线程管理

   * nonatomic(推荐) 性能高

   * atomic(默认)    性能低

 

 4. settergetter方法的名称

   * setter 决定了set方法的名称,一定要有冒号 :

   * getter 决定了get方法的名称(一般用在BOOL类型的get方法)

    // 返回BOOL类型的方法名一般以is开头

    @property (getter = isRich) BOOL rich;

 

 */

 

 

 

 

用来存放数据的类叫做模型类 

 

 

@class

/*

 1.@class作用:

    1> 仅仅告诉编译器,某个名称是一个类的名称;能解决循环包含(#import)问题

  @class Person;  // 仅仅告诉编译器,Person是一个类

    2> 提高性能

 

 2.开发中引用一个类的规范:

  1> .h文件中用@class来声明类

  2> .m文件中用#import来包含类的所有东西(#import要包含类的.h头文件)

 

 

 */

 

两端(对象) 循环引用(循环retain)

循环retain:A对象retain了B对象,B对象retain了A对象

会导致A对象和B对象永远无法释放

解决方案:

1> 一端用retain

2> 另一端用assign

 

内存管理-autorelease

/*  以前创建自动释放池对象的方式

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    // 等价于 @autoreleasepool{

    Person *p = [[[Person alloc] init] autorelease];   // 调用autorelease方法将p放入当前(栈顶)释放池中

    [pool release];                                                // 等价于 }

     */

 

    /*

     错误写法:

      1> alloc之后调用autorelease-----又调用release(野指针错误)

      2> 连续调用多(n)次autorelease-------在自动释放池被销毁时,会对该对象做(n)次release操作(野指针错误)

     

     自动释放池子:

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

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

     */

    // 自动释放池作用:延迟了存放在释放池里面对象的释放时间

    // 好处:不用再关心对象释放的时间---不用再关心什么时候做release操作

    // 缺点:不能精确的控制对象的销毁时间(因此适用于占用内存较小的对象);占用内存较大的对象不宜使用autorelease

    @autoreleasepool

    {  // @autoreleasepool{  代表创建了一个自动释放池对象

        

        Person *p = [[Person alloc] init];    // 此行代码并未将Person对象p放入释放池中

        // autorelease方法返回该对象本身

        // autorelease会将对象放入一个自动释放池(对象)中

        // 调用完autorelease方法后,对象计数器不变

        // 当自动释放池(也是个对象)被销毁时,会对池子里面的所有对象做一次release操作

        [p autorelease];  // 此时将p放入释放池中  上面两句等价于 Person *p = [[[Person alloc] init] autorelease];

        

        @autoreleasepool    // 自动释放池对象可以创建无限个,可以嵌套创建---多个释放池对象以栈形式存放:后进先出(后创建的先销毁)

        {

            // 调用完autorelease方法后,对象计数器不变。  p2计数器 1

            Person *p2 = [[[Person alloc] init] autorelease];

        }

        // 过了上面一行代码,p2计数器 0   p2对象被回收

        

 

    }  // }代表销毁释放池,此时系统会自动对释放池里面的所有对象自动做一次release操作

 

 

 

#import "Person.h"

/* 总结:

  1.系统自带的方法名(以类名开头,不包括前缀)里面没有包含allocnewcopy,说明返回的对象都是autorelease好了的

  2.开发中经常提供一些类方法(以类名开头),快速创建返回一个已经autorelease过的对象

   1> 创建对象时不要直接用类名,一般用self

    // 自定义一个类方法(以类名开头),快速创建一个已经autorelease好了的对象

    + (id)person

    {

        return [[[self alloc] init] autorelease];   // 尽量使用self,那样也可以满足继承自它的子类

    }

 */

@implementation Person

// 自定义一个类方法(以类名开头),快速创建一个已经autorelease好了的对象

+ (id)person

{

    return [[[self alloc] init] autorelease];   // 尽量使用self,那样也可以满足继承自它的子类

}

 

// 自定义一个类方法(以类名开头),快速创建一个已经autorelease好了的,同时初始化了的对象

+ (id)personWithAge:(int)age

{

    //Person *p = [[[Person alloc] init] autorelease];

    Person *p = [self person];     // 尽量使用self,那样也可以满足继承自它的子类

    p.age = age;

    return p;

}

 

@end

posted on 2015-04-08 09:55  我是一匹小黑马  阅读(267)  评论(0编辑  收藏  举报