




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
iOS开发系列Objective-C之内存管理概述我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是由系统自己管理的,放在栈上)。如果一个对象创建并使用后没有得到及时释放那么就会占用大量内存。其他高级语言如C#、Java都是通过垃圾回收来(GC)解决这个问题的,但在OjbC中并没有类似的垃圾回收机制,因此它的内存管理就需要由开发人员手动维护。今天将着重介绍ObjC内存管理:引用计数器属性参数自动释放池引用计数器在Xcode4.2及之后的版本中由于引入了ARC(Automatic Reference Counting)机制,程序编译时Xcode可以自动给你的代码添加内存释放代码,如果编写手动释放代码Xcode会报错,因此在今天的内容中如果你使用的是Xcode4.2之后的版本(相信现在大部分朋友用的版本都比这个要高),必须手动关闭ARC,这样才有助于你理解ObjC的内存回收机制。ObjC中的内存管理机制跟C语言中指针的内容是同样重要的,要开发一个程序并不难,但是优秀的程序则更测重于内存管理,它们往往占用内存更少,运行更加流畅。虽然在新版Xcode引入了ARC,但是很多时候它并不能完全解决你的问题。在Xcode中关闭ARC:项目属性Build Settings-搜索“garbage”找到Objective-C Automatic Reference Counting设置为No即可。内存管理原理我们都知道在C#、Java中都有GC在自动管理内存,当我们实例化一个对象之后通常会有一个变量来引用这个对象(变量中存储对象地址),当这个引用变量不再使用之后(也就是不再引用这个对象)此时GC就会自动回收这个对象,简单的说就是:当一个对象没有任何变量引用的时候就会被回收。例如下面的C#代码片段using System;namespace GC class Program private static void Test() object o=new object(); static void Main(string args) Test(); 上面是一段C#代码,在Test()方法中,通过new Object()创建了一个对象,o是一个对象的引用(存储了对象的地址),它是一个局部变量,作用范围就是Test()方法内部。image当执行完Test()方法之后o就会被释放,此时由于没有变量在引用new Object()这个对象,因此GC会自动回收这个对象所占用的空间。但是在ObjC中没有垃圾回收机制,那么ObjC中内存又是如何管理的呢?其实在ObjC中内存的管理是依赖对象引用计数器来进行的:在ObjC中每个对象内部都有一个与之对应的整数(retainCount),叫“引用计数器”,当一个对象在创建之后它的引用计数器为1,当调用这个对象的alloc、retain、new、copy方法之后引用计数器自动在原来的基础上加1(ObjC中调用一个对象的方法就是给这个对象发送一个消息),当调用这个对象的release方法之后它的引用计数器减1,如果一个对象的引用计数器为0,则系统会自动调用这个对象的dealloc方法来销毁这个对象。下面通过一个简单的例子看一下引用计数器的知识:Person.h/ Person.h/ MemoryManage/ Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import interface Person : NSObject#pragma mark - 属性property (nonatomic,copy) NSString *name;property (nonatomic,assign) int age;endPerson.m/ Person.m/ MemoryManage/ Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import Person.himplementation Person#pragma mark - 覆盖方法#pragma mark 重写dealloc方法,在这个方法中通常进行对象释放操作-(void)dealloc NSLog(Invoke Persons dealloc method.); super dealloc;/注意最后一定要调用父类的dealloc方法(两个目的:一是父类可能有其他引用对象需要释放;二是:当前对象真正的释放操作是在super的dealloc中完成的)endmain.m/ main.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import #import Person.hvoid Test1() Person *p=Person allocinit; /调用alloc,引用计数器+1 =Kenshin; p.age=28; NSLog(retainCount=%lu,p retainCount); /结果:retainCount=1 p release; /结果:Invoke Persons dealloc method. /上面调用过release方法,p指向的对象就会被销毁,但是此时变量p中还存放着Person对象的地址, /如果不设置p=nil,则p就是一个野指针,它指向的内存已经不属于这个程序,因此是很危险的 p=nil; /如果不设置p=nil,此时如果再调用对象release会报错,但是如果此时p已经是空指针了, /则在ObjC中给空指针发送消息是不会报错的 p release;void Test2() Person *p=Person allocinit; =Kenshin; p.age=28; NSLog(retainCount=%lu,p retainCount); /结果:retainCount=1 p retain;/引用计数器+1 NSLog(retainCount=%lu,p retainCount); /结果:retainCount=2 p release;/调用1次release引用计数器-1 NSLog(retainCount=%lu,p retainCount); /结果:retainCount=1 p release; /结果:Invoke Persons dealloc method. p=nil;int main(int argc, const char * argv) autoreleasepool Test1(); return 0;在上面的代码中我们可以通过dealloc方法来查看是否一个对象已经被回收,如果没有被回收则有可能造成内存泄露。如果一个对象被释放之后,那么最后引用它的变量我们手动设置为nil,否则可能造成野指针错误,而且需要注意在ObjC中给空对象发送消息是不会引起错误的。野指针错误形式在Xcode中通常表现为:Thread 1:EXC_BAD_ACCESS(code=EXC_I386_GPFLT)错误。因为你访问了一块已经不属于你的内存。内存释放的原则手动管理内存有时候并不容易,因为对象的引用有时候是错综复杂的,对象之间可能互相交叉引用,此时需要遵循一个法则:谁创建,谁释放。假设现在有一个人员Person类,每个Person可能会购买一辆汽车Car,通常情况下购买汽车这个活动我们可能会单独抽取到一个方法中,同时买车的过程中我们可能会多看几辆来最终确定理想的车,现在我们的代码如下:Car.h/ Car.h/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import interface Car : NSObject#pragma mark - 属性#pragma mark 车牌号property (nonatomic,copy) NSString *no;#pragma mark - 公共方法#pragma mark 运行方法-(void)run;endCar.m/ Car.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import Car.himplementation Car#pragma mark - 公共方法#pragma mark 运行方法-(void)run NSLog(Car(%) run.,self.no);#pragma mark - 覆盖方法#pragma mark 重写dealloc方法-(void)dealloc NSLog(Invoke Car(%) dealloc method.,self.no); super dealloc;endPerson.h/ Person.h/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import class Car;interface Person : NSObject Car *_car;#pragma mark - 属性#pragma mark 姓名property (nonatomic,copy) NSString *name;#pragma mark - 公共方法#pragma mark Car属性的set方法-(void)setCar:(Car *)car;#pragma mark Car属性的get方法-(Car *)car;endPerson.m/ Person.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import Person.h#import Car.himplementation Person#pragma mark - 公共方法#pragma mark Car属性的set方法-(void)setCar:(Car *)car if (_car!=car) /首先判断要赋值的变量和当前成员变量是不是同一个变量 _car release; /释放之前的对象 _car=car retain;/赋值时重新retain #pragma mark Car属性的get方法-(Car *)car return _car;#pragma mark - 覆盖方法#pragma mark 重写dealloc方法-(void)dealloc NSLog(Invoke Person(%) dealloc method.,); _car release;/在此释放对象,即使没有赋值过由于空指针也不会出错 super dealloc;endmain.m/ main.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import #import Person.h#import Car.hvoid getCar(Person *p) Car *car1=Car allocinit; car1.no=888888; p.car=car1; NSLog(retainCount(p)=%lu,p retainCount); Car *car2=Car allocinit; car2.no=666666; car1 release; car1=nil; car2 release; car2=nil;int main(int argc, const char * argv) autoreleasepool Person *p=Person allocinit; =Kenshin; getCar(p); p.car run; p release; p=nil; return 0;程序运行结果:setMethod从运行结果来看创建的三个对象p、car1、car2都被回收了,而且p.car run也能顺利运行,已经达到了我们的需求。但是这里需要重点解释一下setCar方法的实现,setCar方法中为什么没有写成如下形式:-(void)setCar:(Car *)car _car=car;前面在我们说到属性的定义时不是都采用的这种方式吗?根据前面说到的内存释放原则,getCar方法完全符合,在这个方法中定义的两个对象car1、car2也都是在这个方法中释放的,包括main函数中的p对象也是在main函数中定义和释放的。但是如果发现调用完getCar方法之后紧接着调用了汽车的run方法,当然这在程序设计和开发过程中应该是再普通不过的设计了。如果setCar写成“_car=car”的形式,当调用完getCar方法后,人员的car属性被释放了,此时调用run方法是会报错的(大家自己可以试试)。但是如下的方式却不会有问题:-(void)setCar:(Car *)car if (_car!=car) /首先判断要赋值的变量和当前成员变量是不是同一个变量 _car release; /释放之前的对象 _car=car retain;/赋值时重新retain 因为在这个方法中我们通过car retain保证每次属性赋值的时候对象引用计数器+1,这样一来调用过getCar方法可以保证人员的car属性不会被释放,其次为了保证上一次的赋值对象(car1)能够正常释放,我们在赋新值之前对原有的值进行release操作。最后在Person的dealloc方法中对_car进行一次release操作(因为setCar中做了一次retain操作)保证_car能正常回收。属性参数像上面这样编写setCar方法的情况是比较多的,那么如何使用property进行自动实现呢?答案就是使用属性参数,例如上面car属性的setter方法,可以通过property定义如下:property (nonatomic,retain) Car *car;你会发现此刻我们不必手动实现car的getter、setter方法程序仍然没有内存泄露。其实大家也应该都已经看到前面Person的name属性定义的时候我们同样加上了(nonatomic,copy)参数,这些参数到底是什么意思呢?propertyParameterproperty的参数分为三类,也就是说参数最多可以有三个,中间用逗号分隔,每类参数可以从上表三类参数中人选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,默认参数:(atomic,readwrite,assign)一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高;readwirte方法会生成getter、setter两个方法,如果使用readonly则只生成getter方法;关于set方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:assign,用于基本数据类型-(void)setA:(int)a _a=a;retain,通常用于非字符串对象-(void)setA:(Car *)a if(_a!=a) _a release; _a=a retain; copy,通常用于字符串对象、block、NSArray、NSDictionary-(void)setA:(NSString *)a if(_a!=a) _a release; _a=a copy; 备注:本文基于MRC进行介绍,ARC下的情况不同,请参阅其他文章,例如ARC下基本数据类型默认的属性参数为(atomic,readwrite,assign),对象类型默认的属性参数为(atomic,readwrite,strong)自动释放池在ObjC中也有一种内存自动释放的机制叫做“自动引用计数”(或“自动释放池”),与C#、Java不同的是,这只是一种半自动的机制,有些操作还是需要我们手动设置的。自动内存释放使用autoreleasepool关键字声明一个代码块,如果一个对象在初始化时调用了autorelase方法,那么当代码块执行完之后,在块中调用过autorelease方法的对象都会自动调用一次release方法。这样一来就起到了自动释放的作用,同时对象的销毁过程也得到了延迟(统一调用release方法)。看下面的代码:Person.h/ Person.h/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import interface Person : NSObject#pragma mark - 属性#pragma mark 姓名property (nonatomic,copy) NSString *name;#pragma mark - 公共方法#pragma mark 带参数的构造函数-(Person *)initWithName:(NSString *)name;#pragma mark 取得一个对象(静态方法)+(Person *)personWithName:(NSString *)name;endPerson.m/ Person.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import Person.himplementation Person#pragma mark - 公共方法#pragma mark 带参数的构造函数-(Person *)initWithName:(NSString *)name if(self=super init) =name; return self;#pragma mark 取得一个对象(静态方法)+(Person *)personWithName:(NSString *)name Person *p=Person allocinitWithName:name autorelease;/注意这里调用了autorelease return p;#pragma mark - 覆盖方法#pragma mark 重写dealloc方法-(void)dealloc NSLog(Invoke Person(%) dealloc method.,); super dealloc;endmain.m/ main.m/ MemoryManage/ Created by Kenshin Cui on 14-2-15./ Copyright (c) 2014年 Kenshin Cui. All rights reserved./#import #import Person.hint main(int argc, const char * argv) autoreleasepool Person *person1=Person allocinit; person1 autorelease;/调用了autorelease方法后面就不需要手动调用release方法了 =Kenshin;/由于autorelease是延迟释放,所以这里仍然可以使用person1 Person *person2=Person allocinitWithName:Kaoru aut
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 公司流程业务管理制度
- 公司章程经营管理制度
- 公司财务档案管理制度
- 2025年度家具采购合同样本
- 河北省承德县2024-2025学年高一下册期中考试数学试卷附解析
- 广东省广州市2024-2025学年高二下册期中考试数学试卷附解析
- 2025年中考语文(长沙用)课件:微专题精讲 SOLO评价法(分层赋分)
- 2024~2025学年 重庆市高一语文上册第一学月考试试卷附答案
- 智能调度与优化控制-洞察阐释
- 2024年龙岩市新罗区教育局招聘真题
- 2023年新疆初中学业水平考试生物试卷真题(含答案)
- 笔记尤里奇-《HR人力资源转型》
- 管理百年知到章节答案智慧树2023年南昌大学
- 水电站压力钢管安装施工方案
- 这么写网约车事故索赔误工费的起诉状更容易胜诉
- 公安机关业务技术用房建设标准正文
- 兰州卫浴配件项目可行性研究报告
- GA 915-2010讯问椅
- 常见急救知识培训课件
- (人教版教材)初中地理《巴西》完整版
- 律师事务所业务操作规程
评论
0/150
提交评论