第十二篇:OC中的对象内存管理都是对 对象引用计数器 进行管理

1.对象引用计数器:

》占用4个字节

》引用计数器分两类:ARC(Automatic Reference Counting) 自动引用计数(xcode 默认) ,MRC 手动引用计数。

》当创建一个对象时:Iphone * ip = [ [Iphone alloc] init ] ; ip这个对象的引用计数变成1.

》当对象的引用计数是0的时候,系统立即把它回收。

》当一个对象调用 retain 时(前提为MRC,ARC是不允许调用) :[ ip retain ]; ip这个对象的引用计数 + 1 。

》当一个对象调用 release 时(前提为MRC,ARC是不允许调用):[ ip release ] ; ip 的引用计数 - 1。

》获得计数器 , retainCount方法: [ip retainCount];

》当对象ip的引用计数为0时,系统会立即调用dealloc对象方法。如果为MRC时,可以在类中重写dealloc对象方法:

   - (void) dealloc{

       NSLog(@"回收"); //验证

       [当前类特有的成员变量 属于OC类 release] ; // 只要成员变量属于OC类

       [super dealloc]; // 释放从父类继承来的属性对象,一定要写,不然内存回收失败。如果重写先关闭ARC内存管理

    }

》注意:在写程序时,我们只须知道是如何运行就OK,不要去手动管理内存。


2.set方法手动内存管理:

》只要set方法中用了retain 那么就一定要重写dealloc对象方法(该方法是系统释放内存时自动调用)。

(1)形式一,手动写Person对象拥有Car对象:

 - (void)setCar:(Car *)car{

     if ( _car != car) {

            [ _car release]; //当前对象要换其他车,则先对旧车引用计数-1.

           _car = [car retain]; //对新对象car计数+1.

     }

}

(2)形式二,编译器自动生成:@property (nonatomic , retain) Car * car ; //和形式一 一模一样


3.@property 的参数:

(1)set 方法内存管理相关的参数:

retain   : release 旧对象,retain 新对象----(适用于OC对象类型)

assign  : 直接赋值(默认,适用于非OC对象类型)

》copy     : release 旧值,copy新值

(2)是否要生成set方法:

》readwrite   : 同时生成setter 和 getter 的声明、实现(默认)

》readonly    : 只会生成getter的声明、实现

(3)多线程管理:

nonatomic  : 性能高(一般就用这个)

》atomic        : 性能低(系统默认)

(4)setter = functionNam 和 getter = functionNam 来指定方法的名称:

》setter : 决定了 set 方法的名称为functionNam,一定要有个冒号" : "。

》getter : 决定了 get 方法的名称。

》例子:@property (nonatomic,retain , setter = abc:  , getter = getAbc) Car * car ;

              那么自动生成的set / get 方法名 变成了 - (void)abc:(Car *)car ;   - (Car *)getAbc ; 但也可以调用setCar方法来给成员变量 _car 赋值,get方法同理。


4.成员变量的 对象引用计数(MRC) 被循环判断且循环执行dealloc对象方法 和 内存泄漏问题:

》前提:两个类A和B,A类中有类型为B的成员变量 _b,B类中有类型为A的成员变量 _a

》A类的成员变量 _b 的set / get 方法:@property (nonatomic ,retain)B * b ;

   B类的成员变量 _a 的set / get 方法:@property (nonatomic ,retain)A * a ;

   实现了dealloc对象方法:- (void) dealloc{  [ name release] ;   [surper dealloc] ;  } //name = _a 或 _b

》main函数体如下:

   A * pa = [ [A alloc] init ] ;  // pa引用计数 = 1

   B * pb = [ [B alloc] init ] ;  // pb引用计数 = 1

   [pa setB: pb] ;  // pb引用计数 = 2

   [pb setA: pa] ;  // pa引用计数 = 2

// 一个alloc 对应一个 release

   [pb release] ;  // pb引用计数 = 1

   [pa release] ;  // pa引用计数 = 1

(1)问题:

》上面main函数体的代码出现内存泄漏:
     两个对象引用计数 都等于1,在堆里面开辟的空间没有清空。两个对象都没有调用dealloc对象方法。

》如果上面的[pb setA: pa]; 这行代码去掉,那么会在[pa release] ; 这行代码, pa引用计数 = 0 ,会无限循环调用A类dealloc,B类dealloc , _a是个野指针,出现报错。

(2)解决方案:

》两个类中set方法管理内存,一个用retain , 一个用assign。

》在两类中做如下更改:  在A类:@property (nonatomic ,retain)B * b ;

                                          在B类:@property (nonatomic ,assign)A * a ;


5.autorelease与release都是使对象计数器-1,说明:

(1)基本用法:

》对象 = [对象 autorelease] ;

》会将对象放到一个自动释放池中。

》会返回对象本身。

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

》当自动释放池被销毁时,会对池子里面的所有对象做一次release操作。

(2)autorelease 的优点:

》不用再关心对象释放的时间;

》不用再关心什么时候调用relese。

(3)autorelease 的缺点:

》占用内存较大的对象不要随便使用autorelease,因为在开发当中要明确对象释放的时间,一般都会用release。

(4)错误写法:

》alloc之后调用了autorelease,以调用release:

@autoreleasepool
{ // { 开始代表创建了释放池,以栈的形式放入对象
    Person * p = [ [ [ Person alloc ] init ] autorelease ];
    ........
    [p release];

} // } 结束代表销毁释放池 ,野指针 p 又release了一次,报错


》连续调用多次autorelease:

@autoreleasepool
{ // { 开始代表创建了释放池
      Person * p =[ [ [ [Person alloc] init] autorelease ] autorelease ] ;
    ........

} // } 结束代表销毁释放池 ,p第二次release时,野指针 p release一次,报错

(5)自动释放池:

》在IOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在

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

(6)自动释放池的创建方式:

》IOS 5.0以前:

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

    ......

   [pool release] ;

》iOS 5.0开始:

@autorleasepool

{

...........

}

(7)autorelease 实际应用:

》系统自带的方法里面没有包含alloc , new , copy , 说明返回的对象都是autorelease的。例如:[NSString stringWithFormat: .......]

》开发中经常会提拱一些类方法,快速创建一个己经autorelease过的对象;创建对象时不要直接用类名,一般用self

+ (id)person

{

  return [ [ [ self alloc ] init ] autorelease ] ;

}



你可能感兴趣的:(OC基础,IOS移动开发学习笔记)