OC基础教程5-内存管理

前言

  内存管理是程序设计中常见的资源管理(resource management)的一部分。每个计算机系统可供程序使用的资源都是有限的,包括内存,打开的文件以及网络连接等。如果使用了某种资源,比如因打开文件而占用了资源,那么需要随后对其进行清理(这种情况下,关闭文件即可)。
  
  我们必须确保在需要的时候分配内存,在程序运行结束时释放内存。如果我们只分配内存而不释放内存,则会发生内存泄漏(leak memory):程序的内存占用量不断增加,最终会被耗尽并导致程序崩溃。
同样需要注意的是,不要使用任何刚释放的内存,否则可能误用陈旧的数据,从而引发各种各样的错误,而且如果该内存已经加载了其他数据,将会破坏这些新数据。


目标

  理解Objective-C和Cocoa是如何进行内存管理的。


内容

引用计数

  Cocoa采用一种引用计数(reference counting)的技术,有时也叫保留计数(retain counting)来进行计算对象的生命周期。
  每个对象都有一个与之相关联的整数,被称作为引用计数器或保留计数器。
  当某段代码需要访问一个对象时,该代码就将该对象的保留计数器值+1 ,表示我要访问该对象。
  当这段代码结束对象访问时,将对象的保留计数器-1,表示它不再访问该对象。
  当保留计数器的值=0时,表示不再有代码访问该对象了,因此它将被销毁,占用的内存被系统收回。
  
下图表示对象保留计数器的变化:
这里写图片描述

黄金法则

关于内存管理,有一个黄金法则:

内存管理-黄金法则:
  如果对一个对象使用了alloc、[mutable]copy、retain,那么你必须使用相应的release或者autorelease。

类型定义:
  基本类型:任何C的类型,如:int、short、char、long、struct、enum、union等属于基本类型或者结构体;
  内存管理对于C语言基本类型无效;
  任何继承与NSObject类的对象都属于OC类型。
  所有OC对象都有一个计数器,保留着当前被引用的数量。

内存管理对象:
  OC的对象:凡是继承于NSObject;
  每一个对象都有一个retainCount计数器。表示当前的被应用的计数。如果计数为0,那么就真正的释放这个对象。

alloc、retain、release函数:
  1)alloc 函数是创建对象使用,创建完成后计数器为1;只用1次。
  2)retain是对一个对象的计数器+1;可以调用多次。
  3)release是对一个对象计数器-1;减到0对象就会从内存中释放。

增加对象计数器的三种方式:
  1)当明确使用alloc方法来分配对象;
  2)当明确使用copy[WithZone:]或者mutableCopy[WithZone:]来copy对象的时;
  3)当明确使用retain消息。
上述三种方法使得计数器增加,那么就需要使用[auto]release来明确释放对象,也就是递减计数器。

自动释放池

  现在大家都知道,当不再使用对象时必须将其释放,但是弄清楚什么时候不再使用一个对象并不容易。
  我们就想可不可以自动释放。
  于是,Cocoa中就有一个自动释放池(autorelease pool)的东东。在本系列第一个教程中的样本代码中就看到过。@autoreleasepool,从名字上可能看出这是一个和来存放对象的池子(集合),并且能够自动释放里面的对象。
  实际上,当给一个对象发送autorelease消息时,是将该对象添加到了自动释放池中,当自动释放池被销毁时,会向该池中的所有对象发送release消息。
  
  基本上就是说,当基地要被敌人入侵了,就发广播,命令大家“你们自杀吧!”。

这里写图片描述
  自动释放池的原理类似于一个数组的原理,我们每调用一个autorelease就会有个对象挂到内存中去,系统里面就会根据这个池子销毁的时候把池子里面的内容全部销毁。

这里写图片描述
这里写图片描述

垃圾回收

  Objective-C 2.0引入了自动内存管理机制,也称垃圾回收。熟悉Java的孩子应该知道。
  但垃圾回收功能只支持OS X应用开发(桌面应用),无法在iOS应用程序上。所以这里就不提了。

自动引用计数(ARC)

  苹果公司的解决方案里被称为自动引用计数(automatic reference counting,ARC)。顾名思义,ARC会追踪你的对象并决定哪一个仍会使用而哪一个不会再用到,就好像你有了一个负责内存管理的管家或私人助理。
  
  如果你启用了ARC,就只管像平时一样按需分配并使用对象,编译器会帮你插入retain和release语句,无需你自己动手。
  

注意:只是编译器帮你添加retaint 和 release语句,只是编译器级别的处理。

  ARC不是垃圾回收器,ARC只在编译时进行工作。
  而垃圾回收器是在运行时工作。

ARC只对可保留的对象指针(ROPs)有效。可保留的对象指针主要有以下三种:
  1)代码块指针
  2)Objective-C对象指针
  3)通过attribute((NSObject))类型定义的指针。
如果你使用的指针不支持ARC,那么你将不得不亲自手动管理它们。
这样也没有什么问题,因为ARC可以与手动的内存管理共同发挥作用。

想要使用ARC,必须满足以下三个条件:
  1)能够确定哪些对象需要进行内存管理;
  2)能够表明如何去管理对象;
  3)有可行的办法传递对象的所有权。
解释这三个条件:
1)对象的最上层集合知道如何去管理它的子对象。(如果使用C语言基础类型数组就不行)
2)能够对某个对象的保留计数器的值进行+1,-1的操作。(必须为NSObject类的子类)
3)程序要能够在调用者和接收者之间传递所有权。

弱引用

当用某个指针指向某个对象时,你可以管理它的内存(通过retain和release),也可以不管理.
如果管理了,就拥有对这个对象的强引用(strong reference).
如果没管理,那么拥有的就是对这个对象的弱引用(weak reference).
比如,使用assign特性,那么创建的就是一个弱引用。

为什么会有弱引用?
因为要解决保留循环(retain cycle)的问题。

保留循环又是什么鬼?
说个故事来解释:
  假设你拥有一个由其他对象创建,并且保留计数器的值为1的对象A,对象A又创建了保留计数器为1的对象B. 如下图:
这里写图片描述
  因为对象A创建了对象B,所以对象A拥有一个指向对象B的强引用。
  现在,如果对象B有一个指向对象A的强引用,对象A的保留计数器就增加到2。如下图:(保留循环)
这里写图片描述
  有一天,当对象A的拥有者不再需要对象A了,对对象A发送release消息,抛弃了对象A,这样对象A的保留计数器-1,这样对象A还剩1。对象A和对象B的保留计数器都不为0,就都没法被释放掉,于是,出现了内存无聊占用,占着茅坑不拉s,这形成了内存垃圾,叫做内存泄漏。

这里写图片描述

  问题是出现了,怎么解决呢?
  于是弱引用就出场了,使用assign来创建对象B指向对象A的弱引用,这样,保留计数器不会增加。如下图:
这里写图片描述
  问题是解决了,完美么?
  我们再看一个场景:对象A通过强引用指向对象B,而对象C通过弱引用同样指向对象B.
这里写图片描述
  如果对象A释放对象B,这时,对象C将会指向一个错误的地方。
  怎么办?
  解决办法就是让对象自己去清空弱引用对象。这种特殊的弱引用被称为归零弱引用(zeroing weak reference),因为在指向的对象释放之后,这些弱引用就会被设置为零(nil)。如下图:

这里写图片描述


总结+废话

  本文介绍了Cocoa的内存管理方法:retain 、release和autorelease,还讨论了垃圾回收和自动引用计数(ARC).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值