06OC之内存管理
在高级语言中,例如C#是通过垃圾回收机制(GC)来解决这个问题,但是在OC并没有类似的垃圾回收机制,因此必须由程序员手动去维护。今天就讲讲OC中的内存管理:
一、内存管理原理
在Xcode4.2之后的版本,由于引入了ARC(Automatic Reference Counting)机制,程序编译Xcode会自动给你的代码添加内存释放代码,如果编写手动释放代码Xcode会报错,因此在今天的内容,如果使用Xcode4.2以上的版本,必须手动关闭ARC,这样有助于理解OC的内存机制。关闭的步骤如下:
项目设置->Building-setting->搜索garbage找到Objective-C Automatic Reference Counting,设置成No即可。
在C#中有GC给我们自动管理内存,当我们实例化一个对象之后,通常会有一个变量引用这个对象,变量是存储对象的内存地址,当不再需要引用这个对象时,GC就会自动回收这个对象,简单的说,当一个对象没有任何对象引用的时候,就会被回收。
举个简单的例子:
using System;
namespace GC
{
class Program
{
private static void Test()
{
object o=new object();
} static void Main(string[] args)
{
Test();
}
}
}
上述的代码,在Test方法中,通过new Object()创建了一个对象,o是一个对象的引用,它是一个局部变量,作用域为Test方法体内。当在Main方法执行完test,O就会被释放掉,因为没有变量在引用new Object()这个对象,因此GC会自动回收这个对象所占用的空间。
二、引用计数器
在OC中没有GC,那么它的内存是怎么管理呢?其实在OC中内存管理是依赖对象引用计数器来进行的,何为计数器?在OC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,一个对象在创建之后它的引用计数器为1,当调用这个对象的时候(alloc,retain,new,copy)的时候,引用计数器自动在原来的基础上加1,当调用这个对象的release方法之后,它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。
下面我们简单做个例子:
Person.h

Person.m

main.m

执行结果如下:

在这个例子中,我们通过重写了dealloc方法,可以知道一个对象是否已经被回收,如果没有被回收则有可能造成内存泄漏。如果一个对象被释放之后,那么最后引用它的变量我们需要手动式何止为nil,否则有可能造成野指针错误,因为访问了不属于这个程序的内存地址。另外要注意一点,在OC中像空指针发送消息是不会引起任何错误的。
产生野指针报错一般如下:

三、内存释放的原则
手动管理内存有时候并不容易,因为对象的引用错综复杂,可能对象之间又相互引用,这时候就要遵循一个原则:谁创建,谁引用。
假设现在有一个Person类,每个Person都可能买一辆汽车,通常买汽车这个行为我们会封装成一个方法中,同时买车的过程中,我们可能会多看几辆车来确定最终理想的车,代码如下:
Car.h

Car.m

Person.h

Person.m

Main.m

执行结果如下:

需要注意的有:
car1和car2都被回收了,然而我们执行p.car run却不会报错,这就达到了我们的目的。首先我们要先解释下,为什么我们的setCar不是写成这种形式?
-(void)setCar:(Car *)car{
_car=car;
}
因为我们之前讲的属性赋值都是采用这种方式的啊。根据前面所说的内存释放原则,getCar方法完全符合,在方法中定义的两个对象car1、car2也都是在这个方法中释放的,main函数的P也是在main函数中定义个释放的。
然而我们是写成这个样子:
-(void)setCar:(Car *)car{
if (_car!=car) { //首先判断要赋值的变量和当前成员变量是不是同一个变量
[_car release]; //释放之前的对象
_car=[car retain];//赋值时重新retain
}
}
在这个方法中我们通过调用car retain可以保证每次属性赋值的时候对象的引用计数器+1,这样以来调用过getCar方法可以保证人员的Car属性不会被释放,同时呢,也可以保证上一次的赋值对象Car1能够正常的释放,因为我们在赋新值的时候,进行了release操作。最后在Person的dealloc方法中对_car进行了一次realse操作(因为在setCar时候做了一次的retain操作),就可以保证_car正常回收。
四、Propety(属性参数)
如上,setCar方法的情况是比较多的,那么如何使用@propety自动实现呢?答案是使用属性参数,例如上面的setter方法,可以通过@property定义如下:
@property (nonatomic,retain) Car *car;
这样子我们不需要手动去实现car的setter方法,setter方法也不会导致内存泄漏,其实大家可以看到前面Person的name属性也加上了(nonatomic,copy)属性,这些参数是什么意思呢?

如下所示,property的参数分为三类,也就是说参数最多只能有三个,中间用逗号隔开,每类的参数可以从上表中的参数中任选一个,如果不进行设置,会取以上三类的默认参数,默认参数为(atomic,readwrite,assign)
一般情况下,如果在多线程开发中一个属性可能会被两个或者两个以上的线程同时访问,这个时候就要考虑atomic,相当于加锁,否则的话,建议使用nonatomic,不用加锁,效率够高。readwrite方法会生成getter、setter两个方法,如果只是readonly只会生成getter方法,关于set方法处理需要特别说明一下,假如我们定义一个属性a,这里列出以上三种生成方案:
1、assign,用于基本类型
-(void)setA:(int)a{
_a=a;
}
2、retain,常用于非字符串对象
-(void)setA:(Car *)a{
if(_a!=a){
[_a release];
_a=[a retain];
}
}
3、copy,常用于字符串对象,block,NSArray,NSDictonary
-(void)setA:(NSString *)a{
if(_a!=a){
[_a release];
_a=[a copy];
}
}
备注:
这是基于MRC进行介绍,ARC的情况不一样,例如ARC的基本数据类型默认的属性参数为(atomic,readwrite,assign),对象类型的默认属性参数为(atomic,readwrite,strong)
五、自动释放池
在OC中,也有一种内存自动释放的机制,叫做:“自动引用计数”或者“自动释放池”,然而,与C#的GC不一样的是,它是一个半自动的,有些操作需要我们手动去设置。自动内存释放我们使用@autoreleasepool关键字来声明一个代码块,如果一个对象在初始化的时候调用了autorelease方法,那么当代码块的代码执行完毕之后,在块中调用过autorelease方法对象都会自动调用一次release方法,这样一来就起到了自动释放的作用,同时对象的销毁过程也得到了延迟,因为统一调用了release方法。
Person.h

Person.m

Main.m

让我们看下执行结果:

如上结果所示,三个对象都得到了释放,但是person4并没有释放,原因很简单,因为我们手动retain了一下,计数器加1,当自动释放池统一release对象,但是Peron4没有释放,这就造成了内存泄漏。再说Person1,autorelease方法只是将对象的内存释放延迟到了自动释放池销毁的时候,因此person1,调用完autorelease之后,它还存在的,因此给它赋值不会有任何问题。在OC中通常一个静态方法返回一个对象本身的话,在静态方法我们需要调用autorelease方法,因为按照内存释放原则,在外部使用不应该对操作这个静态对象的release方法或者autorelease方法,因此这个调用需要在静态方法内部完成。
总结下内存管理:
1、autorealease方法不会改变对象的引用计数器,只是将这个对象放在自动释放池中。
2、自动释放池的本质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象,只是-1的操作,如果计数器>1,该对象则无法销毁。
3、由于自动释放池最后统一销毁对象,因为如果一个操作比较占用内存,对象比较多,或者对象占用资源比较多,最好不要放在自动释放池中,或者考虑放在多个自动释放池中。
4、OC中类库的静态方法一般都不需要手动释放,因为内部已经调用了autorelease方法。
06OC之内存管理的更多相关文章
- .NET基础拾遗(1)类型语法基础和内存管理基础
Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...
- PHP扩展-生命周期和内存管理
1. PHP源码结构 PHP的内核子系统有两个,ZE(Zend Engine)和PHP Core.ZE负责将PHP脚本解析成机器码(也成为token符)后,在进程空间执行这些机器码:ZE还负责内存管理 ...
- linux2.6 内存管理——逻辑地址转换为线性地址(逻辑地址、线性地址、物理地址、虚拟地址)
Linux系统中的物理存储空间和虚拟存储空间的地址范围分别都是从0x00000000到0xFFFFFFFF,共4GB,但物理存储空间与虚拟存储空间布局完全不同.Linux运行在虚拟存储空间,并负责把系 ...
- linux2.6 内存管理——概述
在紧接着相当长的篇幅中,都是围绕着Linux如何管理内存进行阐述,在内核中分配内存并不是一件非常容易的事情,因为在此过程中必须遵从内核特定的状态约束.linux内存管理建立在基本的分页机制基础上,在l ...
- Objective-C内存管理之引用计数
初学者在学习Objective-c的时候,很容易在内存管理这一部分陷入混乱状态,很大一部分原因是没有弄清楚引用计数的原理,搞不明白对象的引用数量,这样就当然无法彻底释放对象的内存了,苹果官方文档在内存 ...
- Quartz2D内存管理
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "PingFang SC"; color: #239619 } p.p2 ...
- 浅谈Linux内存管理机制
经常遇到一些刚接触Linux的新手会问内存占用怎么那么多?在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这 ...
- linux内存管理
一.Linux 进程在内存中的数据结构 一个可执行程序在存储(没有调入内存)时分为代码段,数据段,未初始化数据段三部分: 1) 代码段:存放CPU执行的机器指令.通常代码区是共享的,即其它执行程 ...
- cocos2d-x内存管理
Cocos2d-x内存管理 老师让我给班上同学讲讲cocos2d-x的内存管理,时间也不多,于是看了看源码,写了个提纲和大概思想 一. 为什么需要内存管理 1. new和delete 2. 堆上申 ...
随机推荐
- 2.bootstrap练习笔记-轮播图
bootstrap练习笔记-轮播图 1.要使用轮播图,首先要将其放在一个主div里面 设置id为myCaroysel class为carousel slide 设置id是标识这个div是轮播图,等到l ...
- [No00007C]dreamweaver cc 注释快捷键
设置快捷键入口:编辑->快捷键 打开后: 先复制一份快捷键拷贝(系统自带的不让修改), 选择代码编辑 下拉到后面,设置快捷键
- [No000079]罗辑思维2016.1.2日前的所有每日语音,python3做的网络爬虫
源码地址:https://github.com/charygao/Download_the_LouJiSiWei 写过很久了,vision1.0里有不少bug,今天重新整理修改了一下,运行了一下,2个 ...
- [No00006D]下载离线版的github for windows【以Github for Windows 3.0.110.为例】
目录 先上地址后讲原理: 原理: 11个目录的文件怎么一口气下载呢? 最后,把下好的文件批量名,同时将GitHub.exe.manifest也放到软件根目录下(与GitHub.exe同级): 今后的猜 ...
- MVC4 自定义错误页面(转)
一.概述 MVC4框架自带了定义错误页,该页面位于Shared/Error,该页面能够显示系统未能捕获的异常,如何才能使用该页面: 二.使用步骤: 1.配置WebConfig文件,在System.We ...
- arduino红外遥控库IRremote的IRsend类sendRaw函数溢出问题及其解决方法
最近在调试红外遥控格力空调,在论坛中学到了不少东西.参考: (1)<解决问题系列(4)——红外编码分析利器使用> (2)<315Mhz模块传输替代315Mhz遥控器> 调试环境 ...
- 玩蛇记--Python处理海量手机号码
一.任务描述 上周,老板给我一个小任务:批量生成手机号码并去重.给了我一个Excel表,里面是中国移动各个地区的可用手机号码前7位(如下图),里面有十三张表,每个表里的电话号码前缀估计大概是八千个,需 ...
- java用selenium库控制chrome
一.简介 selenium是浏览器自动化工具,可以通过程序来控制浏览器行为,这就是selenium!你可以用它做任何你想做的事情.很多时候人们用它测试web应用,但selenium的用途绝不仅限于此. ...
- 76 mkswaP-用于设置交换区
Linux mkswap命令用于设置交换区(swap area). mkswap可将磁盘分区或文件设为Linux的交换区. 语法 mkswap [-cf][-v0][-v1][设备名称或文件][交换区 ...
- JavaScript语法规范
推荐的JavaScript编码规范 阅读 247 评论 0 喜欢 0 作为前端开发人员,我相信每一个人都或多或少的用到原生的JavaScript,也正是因为用的人多,导致编码风格也是多种多样的,而不规 ...