该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
本节接着介绍内存管理相关的内容;
第4章 内存管理
4.4 使用自动引用计数
在使用MRR内存管理方式时,你会亲自管理程序中对象回收工作;
好处是可以精细地控制内存的使用情况;
坏处是大幅度增加了开发人员的负担;
因此,手动引用计数更容易出错,会导致程序泄漏内存或崩溃;最好能使用工具完成这类基本任务;
自动引用计数(ARC):
是一种功能强大的内存管理工具,是这类任务自动化;
ARC使用引用计数的模式与MRR使用的相同,但是由编译器管理回收对象的工作;
具体的:
在编译程序时,编译器会分析源代码,确定以动态方式创建的对象的回收需求,然后在已编译代码的必要位置自动插入retain和release消息;
APR还可以(潜在地)提升应用的性能和消除内存管理错误(如错误释放仍在使用的对象、保留不再使用的对象);
此外,和垃圾回收机制相比,ARC更可靠(保留和释放语句是在编译时插入的),并且不会为实现垃圾回收机制而在程序执行过程中引入暂停操作;
ARC可以为OC对象和块提供自动内存管理功能;
注意,ARC无法自动处理循环引用;
为此,OC提供了弱引用功能,在必要时通过该功能你可以手动解消循环引用;
4.4.1 使用ARC的规则和约定
与MRR相比,ARC可大幅度简化内存管理工作;
使用ARC时应遵守规则:
1)不能手动编写发送retain、retainCount、release、autorelease和dealloc消息的代码;
ARC不允许手动方式控制对象的回收工作;
如果需要管理实例变量之外的资源,可以手动编写dealloc方法(比如注销通知);
ARC在您没有在类中编写dealloc方法的情况下,会自动创建该方法,释放其中的对象,并调用父类的dealloc方法;
在类中编写dealloc方法的情况下,是不能再调用[super dealloc]的;
2)不能直接进行id和(void *)类型的互转;
ARC只能管理OC对象和块,因此编译器只能处理能够识别出其类型的对象;
因为(void *)类型的指针是能够转换为任何指针类型(包括OC中没有的指针类型)的通用指针;所以必须设置这个限制;
当使用Foundation框架对象和Core Foundation对象(一种提供C语言API的Apple软件库)互相转换类型时,此种情况较常见,会有相应的API供在ARC和非ARC环境之间进行变量所有权的转移;
3)需要使用自动释放池代码块执行由ARC管理的自动释放操作;
4)不能调用Foundation框架函数NSAllocateObject和NSDeallocateObject;
这两个函数提供了在指定内存区域为对象分配和释放内存的功能;因为OC不再支持内存区,所以无法使用它们;
5)无法使用C结构中的对象指针;
ARC不能为动态分配的C结构执行内存管理操作,因为编译器无法判断何时应插入必需的retain和release消息;
6)不能使用内存区(NSZone);如前所述,OC不再支持内存区;
7)为了与非ARC代码协作,不能创建以copy开头的方法和自动声明属性(除非明确定义了其他读取器);
8)默认情况下,ARC并非异常安全的;
无法释放异常失效的__strong变量;
无法在完整表达式抛出异常时,将位于该完整表达式末尾的对象释放;
使用编译器选项-fobjc-arc-exceptions可以启动处理ARC代码异常的功能;除非编译器解决了该异常,否则当弱引用异常失效时,ARC肯定会释放弱引用变量;
上边都是一些规则,有的比较难懂,在不断学习和成长中,慢慢体会吧;
4.4.2 ARC的声明周期限定符
OC提供了一系列专用的ARC限定符,使用它们可以声明常规变量和属性的生命周期;
应用于常规变量的限定符:
1)__strong:
表明使用任何alloc/init消息创建的对象都会在其作用范围内被保留;
“作用范围”通常是指变量声明所在花括号对的内部(如方法、循环、条件语句块);
这是常规变量的默认设置;
2)__weak:
表明对象随时都可以被释放;
只有当对象拥有其他强引用时,该标记才会有用处;(因为weak是弱引用)
对象被释放之后,待__weak限定符的变量会被设置为nil;
3)__unsafe_unretained:
与__weak限定符类似,但是在对象被释放后,指针不会被置为nil,而是处于悬空状态(不再指向合法对象);
所以是不安全的 也不会获得对象的所有权的;
4)__autoreleasing:
不要将该限定符与调用对象中的autorelease方法搞混了,这个限定符用于通过引用传递对象;
使用ARC生命周期限定符声明OC的对象变量时,语法如下:
类名 * 限定符 变量名;
如果没有设置限定符,系统会使用默认值__strong;
应用于属性的ARC生命周期限定符:
1)strong:
等同于retain特性,获取对象所有权;
2)weak:
类似于assign特性,无数次被问到过assign和weak的区别,两者都是简单赋值操作,区别在于,weak特性限定的,如果引用对象被释放了,其实例变量会被设置为nil;
ARC中,strong是对象型属性默认的所有权特性;
(后续会详细介绍一下属性的特性)
4.4.3 使用ARC
ARC中类的init方法(无对象变量赋值)和MRR中的一样;
但dealloc方法不同,ARC中的不调用父类的dealloc方法,因为这是由ARC自动执行的操作;
给出一个例子:
MRR:
-initWithName:(NSString *)name{
if(self = [super init]){
_name = name;
[_name retain];
}
return self;
}
-(void)dealloc{
[_name release];
[super dealloc];
}
ARC:
-initWithName:(NSString *)name{
if(self = [super init]){
_name = name;
}
return self;
}
-(void)dealloc{
}
通过ARC内存管理方式创建的对象,不再使用时(比如置为nil,或作用范围结束),ARC会自动释放它们;
ARC还会管理必需的依存对象,避免出现正在使用对象的情况;
4.4.4 避免循环引用
OC的引用计数模型是通过获取对象所有权(通过retain消息),以及再不使用对象后释放对象的所有权(通过release)实现的;
ARC自动化了该过程;
它会根据需要在代码中自动插入retain/release/autorelease消息;
然而,如果两个对象直接或间接相互引用,就会导致循环引用问题;
比如,类A的对象a拥有一个类B的实例,如果类B的这个实例对象也拥有类A的对象实例,就会在这两个对象之间造成循环引用;
两个对象将永远不会被释放;
解决方式是使用弱引用:
弱引用是一种非所有权引用,被弱引用的对象不属于引用它的对象,从而消除循环引用;
OC约定:
父对象强引用其所有子对象;
子对象弱引用其父对象;
因此上述的问题可以这样修改:
@Interface A:NSObject{
@public B * b;
}
@Interface B:NSObject{
@public A * __weak a;
}
当B类对象被释放时,变量a也会被置为nil;避免了循环引用;
4.5 小结
本章介绍了:
OC中的内存管理,包括:
OC内存模型;
为OC程序分配和释放内存的方式;
以及两种OC内存管理机制的用法;
要点:
1)在运行时,OC程序创建的对象(通过NSObject类的alloc方法)会以动态方式存储在预先分配的内存区域中,这片内存区域称为堆内存;
以动态方式创建对象意味着需要管理内存,因为在堆内存中创建的对象会一直使用该区域中的内存;
不进行内存管理或者采用错误的内存管理方式,通常会导致内存泄漏和悬挂指针问题;
2)OC的内存管理是使用引用计数实现的;
该技术通过对象的唯一引用判断对象是否正在被使用;
如果某个对象的引用计数变为0,那么就会被认为不再有用;运行时系统会释放它占用的内存;
3)OC开发环境有两种内存管理机制:手动管理(MRR)和自动引用计数(ARC);
4)在使用MRR内存管理方式时,需要编写确切的代码,管理对象的生命周期、获取对象(你创建的或需要使用的)所有权和释放对象(不再需要的)所有权;
5)ARC使用的引用计数模型与MRR使用的引用计数模型相同,不同的是ARC通过编译器自动管理对象的生命周期;
在编译程序时,编译器会分析源代码,确定以动态方式创建的对象的生命周期,然后在已编译的代码中自动插入必需的retain和release消息;
6)ARC中增加了新的对象生命周期限定符,使用这些限定符可以确切地声明对象变量和属性的生命周期,还可以实现弱引用,避免出现循环引用;
精巧的OC程序仅会占用它所必需的内存,不会使内存泄漏和尝试访问已经失效的对象;
接下来做好准备,迎接下一章——预处理器。
该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!
上一节介绍了消息传递和消息转发,接下来我们看看内存管理相关的内容;