OC_内存管理:MRC与ARC

内存管理

 

 

   1.1内存管理

1.1.1 C的内存管理,以及麻烦之处

 

char *p = (char *)malloc(100*sizeof (char)); 

这是C的动态内存分配,我们手动跟系统申请了100个字节的内存;或者说系统在堆里开辟了100个字节的空间,并将这个空间的首地址返回给指针变量p。

free(p);

使用完成后,手动跟系统释放内存空间;或者说系统回收空间。

如上就是C里简单的内存管理。

 

 

C的内存管理,我们手动申请,手动释放。这样来看,我们只需要注意三个问题就好了:

1,申请内存,使用完成后需要释放,如果不释放会造成内存泄露。

2,不能多次释放,如果多次释放,则会崩溃。

3,不能提前释放,如果提前释放了再使用,也会崩溃。

 

但是,如果项目比较复杂,需要有几十上百号人一起分工完成,就很容易出现问题。

比方说我们开辟了一块内存空间里存放了一块很有用的数据。但是,这个数据不只有我在这一块代码里用,甚至有多个人,在程序的多个地方使用。这样造成的结果就是,就算我使用完成这块内存,我也不能去释放他,因为我不能确定,别人在别的地方是否还需要使用这块内存。内存泄露在所难免了。

 

 

 

 

 

1.1.2  OC的内存管理

                         引用计数(Reference Count)/保留计数(retain Count)

对于一块动态申请的内存,有一个人(指针)使用,就给这个内存的计数器加1,使用完成后,就给这个计数器减1,当这个内存的引用计数为0了,我们再释放他,这样,上面的问题就解决了。OC,就是使用引用计数这种方式来管理内存的。

 

 

            1.1.3 MRC和ARC

ARC Automatic Reference Counting,自动引用计数,由Xcode,帮我们去管理内存。

MRC Manual  Reference Counting,手动引用计数,我们手动管理内存。

 

Xcode 5.0  版本以后默认是ARC模式.

 

 

 

 

     1.2  MRC

 

1.2.1 内存管理的黄金法则

对于引用计数来说,有一套内存管理的黄金法则:

The basic rule to apply is everything that increases the reference counter with alloc, [mutable]copy[withZone:] or retain is in charge of the corresponding [auto]release.

凡是用alloc,retain,new,copy,mutableCopy或者以copy开头,以mutableCopy开头的方法[创建]的对象,都需要用release或autorelease进行释放。

 

通俗一点的说法就是谁污染谁治理(谁创建谁释放)。

 

 

1.2.2 如何将工程改为MRC

Xcode5,工程创建的时候是ARC的,我们如果想要MRC,需要进行如下设置。

选中工程 - target - Bulid Settings -Automatic Reference Counting改为NO。(搜索retain或gar)

 

 

    1.2.3  alloc与release

ARC模式下创建一个Dog类

@interface Dog : NSObject

  @end

  @implementation Dog

  - (void)dealloc

  {

    NSLog(@"dog dealloc");

    //[super dealloc];

  }

  @end

  dealloc是析构函数,当对象销毁的时候,会自动调用这个方法,我们在这里重写这个方法。

 

  在main函数里,写入如下代码:

  int main(int argc, const char * argv[])

  {

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     }

    NSLog(@"程序即将退出");

    return 0;

  }

  从终端打印信息来看,“程序即将退出”这条打印之前,已经打印dog dealloc,也就是说在程序运行结束前,dog对象已经销毁了。这个是ARC,由Xcode帮我们管理dog对象。

 

  将ARC改为MRC,再执行程序,dog对象并没有销毁,因为我们现在是手动管理了,我们需要遵守内存管理的黄金法则,Dog *dog = [[Dog alloc] init]; 我们需要对dog进行release。将main函数代码改为如下形式:

int main(int argc, const char * argv[])

{

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

[dog release];

     }

    NSLog(@"程序即将退出");

    return 0;

}

再次执行程序,从打印可以看出,dog对象已经销毁。这就是黄金法则,我们对dog进行alloc,就要对dog进行release。

注意,release 并不是销毁对象,让对象的引用计数减1,当对象的引用计数为0的时候,自动调用dealloc方法,销毁对象。

 

 

 

1.2.4 retain与retainCount

retain,将对象进行保留操作,也就是使对象的引用计数加1。

retainCount,打印一个对象的引用计数。

 

 

 

1.2.5  类的组合中使用

在上面代码中,增加Person类

@interface Person : NSObject {

  // 一个人,养了一条狗(持有一条狗)

    Dog *_dog;

  }

  - (void)setDog:(Dog *)dog;

  - (Dog *)dog;

  @end

  @implementation Person

  /* 版本1 (有问题) 人并没有真正持有狗,如果在main函数里[dog release],让dog的引用计数减1,就变为0,dog就销毁了。

    - (void)setDog:(Dog *)dog

  {

    _dog = dog;

  }

    */

 

  /* 版本2 (有问题) 如果人再持有别的狗,就会造成第一条狗得不到释放,内存泄露。

  - (void)setDog:(Dog *)dog

  {

    _dog = [dog retain];

  }

    */

 

  /* 版本3 (有问题) 如果本来持有一条狗,又重新设置这条狗,先进行release,这个时候,很可能dog就销毁了,然后,就没法再次retain了。

  - (void)setDog:(Dog *)dog

  {

    [_dog release];

    _dog = [dog retain];

  }

    */

 

  // 版本4 OK!,标准写法

  - (void)setDog:(Dog *)dog

  {

    if (_dog != dog) {

                         [_dog release];

                         _dog = [dog retain];

      }

  }

 

  - (Dog *)dog

  {

return _dog;

  }

 

  - (void)dealloc

  {

    NSLog(@"person dealloc");

// 人在销毁的时候,一并将持有的dog对象销毁

    [_dog release];

    [super dealloc];

  }

 

 

1.2.6 @property retain,assign,copy展开

retain展开

如上代码里,Person的setter和getter方法,也可以用property,写成如下形式

@property (retain) Dog *dog;

             则会展开如下:

- (void)setDog:(Dog *)dog

{

if (_dog != dog)

{

[_dog release];

_dog = [dog retain];

}

}

 

 

 

          assign展开

           //简单数据类型 ,OC的内存管理对于简单的数据类型 int\float…, 

  @property ( assign) int  a;,assign是直接赋值,则会展开如下:

- (void)setDog:(int) a

{

_a = a;

}

 

 

copy展开  , 复制一份原来的对象

//copy 多用于字符串

@property (copy)NSString *name;

           展开如下:

- (void)setName:(NSString *)name

{

if (_name != name)

{

[_name release];

_name = [name copy];

}

}

 

 

 

1.2.7 数组的内存管理

  1)当我们创建数组的时候,数组会对每个对象进行引用计数加1

  2)当数组销毁的时候,数组会对每个对象进行引用计数减1

  3)当我们给数组添加对象的时候,会对对象进行引用计数加1

  4)当我们给数组删除对象的时候,会对对象进行引用计数减1

  总之,谁污染谁治理,管好自己就可以了(数组内部也遵守内存管理)。

 

 

1.2.8  autorelease与autoreleasepool

在main函数里写如下代码:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

                  Dog *dog = [[Dog alloc] init];

 

//dog并没有马上销毁,而是延迟销毁,将dog对象的拥有权交给了autoreleasepool

[dog autorelease];

 

//这个是可以打印的,因为打印完dog的引用计数后,dog对象才销毁

NSLog(@"retainCount = %lu",dog.retainCount);

     }

    NSLog(@"程序即将退出");

    return 0;

  }

  autoreleasepool相当于一个数组,如果哪个对象发送autorelease消息,实际将对象的拥有权交给了autoreleasepool;当autoreleasepool销毁的时候,autoreleasepool里持有的对象都发送一个release消息。

 

 

 

        1.2.9  对于自动内存释放简单总结一下:

  1.                1 autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
  2. 自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(例如如果一个对象的引用计数器>1则此时就无法销毁);
  3. 由于自动释放池最后统一release,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池;

 

 

 

 

  1.3  ARC

         //从Xcode5以后,默认自动内存管理

        automatic reference/retain  counting;

       1.3.1自动引用计数

     【提】简单点说就是让编译器完成堆空间的引用计数加减,自动释放。程序员不再写 retain release等方法

     【另】OC的自动内存管理,不同于JAVA垃圾回收。而是在预处理时,直接在应该保留的地方,添加retain,在应该释放的地方,添加release。是直接添加代码。

 

    【另】从效率上,ARC优于手动内存管理。

 

 

        1.3.2 MRC与ARC混编

                1.MRC代码转成ARC代码

              Edit —> Convert —> To Objective-c ARC

                2.ARC非ARC混编

              //同一个工程中,部分文件使用ARC,部分文件不使用ARC。

                   Build phase -----> Complie Source

                   -fno-objc-arc  (对指定文件不使用ARC)

 

        1.3.3  ARC模式下的关键字:

__strong/__weak/__unsafe_unretain

    

strong(强引用) 缺省属性,其修饰的对象指针,指向哪个对象,会对该对象retain,离开哪个对象,会对该对象release。

 

weak(弱引用)其修饰的对象指针,指向任何对象都不会retain。这样的指针指向的对象随时可能消失。如果对象消失了,这个指针会自动变成nil。

      //在iOS编程中,代理对象使用弱引用。

 

unsafe_unretained 其修饰的对象指针,指向任何对象都不retain。当指向的对象消失,该指针不会变成nil,仍然指向已经释放的对象.不建议使用。

 

 

         //实际工作中,不是字符串,写strong,代理对象写weak,字符串写copy

 

转载于:https://www.cnblogs.com/yuhanchen/p/5243403.html

你可能感兴趣的:(OC_内存管理:MRC与ARC)