iPhone开发之深入浅出 (7) — ARC总结
原文链接:http://www.yifeiyang.net/development-of-the-iphone-simply-7/
通过前面几篇文章的介绍,我想大家应该对ARC有了一个比较完整的理解。最后,我们来对ARC做一个总结,并把一些未涉及到的细节部分再深入讨论一下。
内存管理基本原则
- 内存管理的依循下面的基本原则
- 自己生成的对象,那么既是其持有者
- 不是自己生成的对象,也可成为其持有者(一个对象可以被多个人持有)
- 如果不想持有对象的时候,必须释放其所有权
- 不能释放已不再持有所有权的对象不管ARC有没有效,该原则始终存在。
所有权关键字
从代码上看,有ARC的代码和没有ARC的代码区别就在下面的几个关键字。
类似 NSObject* 的对象类型,或者 id 类型1,当ARC有效的时候,根据具体情况,这些关键字必须要使用2。
- __strong
- __weak
- __unsafe_unretained
- __autoreleasing__strong是默认的修饰符。__weak修饰了一个自动nil的weak引用。__unsafe_unretained声明了一个不会自动nil的weak引用。当变量被释放,那么它就变成了一个野指针了。__autoreleasing 用来修饰一个声明为 (id *) 的函数的参数,当函数返回值时被释放。
接下来,我们结合下面ARC的使用准则,来看看一些使用ARC后的技术细节。
ARC使用准则
为了比秒程序秒退的尴尬,ARC有效时,我们的代码必须遵循下面的准则。
- 不能使用 retain/release/retainCount/autorelease
- 不能使用 NSAllocateObject/NSDeallocateObject
- 不能使用 NSZone
- 不能明示调用dealloc
- 内存管理相关的函数必须遵循命名规则
- 使用@autoreleasepool代替NSAutoreleasePool
- Objective-C 对象不能作为C语言结构体(struct/union)的成员
- 【id】与【void*】之间需要明示cast
建议使用Objective-C的class来管理数据格式,来代替C语言的struct。不能隐式转换 id 和 void *。
- 让我们一个一个来分析
不能使用
retain/release/retainCount/autorelease
内存管理完全交给编译器去做,所以之前内存相关的函数(retain/release/retainCount/autorelease)不能出现在程序中。Apple的ARC文档中也有下面的说明。
ARC 有效后,不需要再次使用retain 和 release
如果我们在程序中使用这些函数,经得到类似下面的编译错误信息。
- error: ARC forbids explicit message send of ’release’
- [o release];
- ^ ~~~~~~~
不能使用
NSAllocateObject/NSDeallocateObject
生成并持有一个Objective-C对象的时候,往往像下面一样使用NSObject的alloc接口函数。
- id obj = [NSObject alloc];
实际上,如果我们看了GNUstep 中关于 alloc 的代码就会明白,实际他是使用 NSAllocateObject 来生成并持有对象实例的。换言之,ARC有效的时候,NSAllocateObject函数的调用也是禁止的。如果使用,也会遇到下面的编译错误。
- error: ’NSAllocateObject’ is unavailable:
- not available in automatic reference counting mode
同样,对象释放时使用的 NSDeallocateObject 函数也不能使用。
不能使用
NSZone
NSZone 是什么?NSZone 是为了防止内存碎片而导入的一项措施。Zone 是内存管理的基本单元,系统中管理复数的Zone。系统根据对象的使用目的,尺寸,分配其所属的Zone区域。以提高对象的访问效率,避免不必要的内存碎片。但是,现在的运行时系统(用编译开关 __OBJC2__ 指定的情况下)是不支持Zone概念的。所以,不管ARC是否有效,都不能使用 NSZone。
不能明示调用dealloc
不管是否使用ARC,当对象被释放的时候,对象的dealloc函数被调用(就像是C++中对象的析构函数)。在该函数中,需要做一些内存释放的动作。比如,当对象中使用了malloc分配的C语言内存空间,那么dealloc中就需要像下面一样处理内存的释放。
- - (void) dealloc
- {
- free(buffer_);
- }
又或者是注册的delegate对象,观察者对象需要被删除的时候,也是在dealloc函数中动作。
- - (void) dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
如果在ARC无效的时候,我们还要像下面一样,调用父类对象的dealloc函数。
- - (void) dealloc
- {
- [super dealloc];
- }
但是当ARC有效的时候,[super dealloc];的调用已经被编译器自动执行,已经不需要我们明示调用了。如果你在代码中还这样写,难免遇到下面的错误。
- error: ARC forbids explicit message send of ’dealloc’
- [super dealloc];
- ^ ~~~~~~~
内存管理相关的函数必须遵循命名规则
在iPhone开发之深入浅出 (3) — ARC之前世今生中,我们知道如果是 alloc/new/copy/mutableCopy/init 开头的函数,需要将对象所有权返回给调用端。这条规则不管ARC是否有效都应该被遵守。只是 init 开头的函数比较特殊,他只在ARC下有要求,而且异常苛刻。
init 开始的函数只能返回id型,或者是该函数所属的类/父类的对象类型。基本上来说,init函数是针对alloc函数的返回值,做一些初始化处理,然后再将该对象返回。比如:
- id obj = [[NSObject alloc] init];
再比如下面定义的函数就是不对的:
- - (void) initThisObject;
需要是下面这样:
- - (id) initWithObject:(id)obj;
另外,下面名为 initialize 的函数比较特殊,编译器将把它过滤掉,不按上面的规则处理。
使用@autoreleasepool代替NSAutoreleasePool
在ARC之下,已经不能在代码中使用 NSAutoreleasePool,我们之前写 main.m 文件的时候,往往像下面这样写。
- int main(int argc, char *argv[]) {
- NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, nil);
- [pool release];
- return retVal;
- }
而当ARC有效后,我们需要用@autoreleasepool代替NSAutoreleasePool。
- int main(int argc, char *argv[])
- {
- @autoreleasepool {
- return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- }
- }
当编译器看到 @autoreleasepool 定义的块后会自动生成 NSAutoreleasePool 对象,并将需要的对象放入 AutoReleasePool 中,当出方块的定义范围时,pool 中的对象将被释放。
Objective-C
对象不能作为C语言结构体(struct/union)的成员
当我们设置ARC有效,并在C语言的结构体中定义Objective-C的对象时,将出现类似下面的编译错误。
- struct Data {
- NSMutableArray *array;
- };
- error: ARC forbids Objective-C objs in structs or unions
- NSMutableArray *array;
- ^
由于 ARC 是将内存管理的细节委托给编译器来做,所以说编译器必须要管理对象的生命周期。而LLVM 3.0中不存在对单纯C语言构造体成员的内存管理方法。如果单纯是栈对象,利用进出栈原理,可以简单地维护对象的生命周期;而结构体是不行的,简单地理解,结构体没有析构函数,编译器自身不能自动释放其内部的 Objective-C 对象。
当我们必须在C语言的结构体中放入 Objective-C 对象的时候,可以使用 void* 转型,或者使用 __unsafe_unretained 关键字。比如下面:
- struct Data {
- NSMutableArray __unsafe_unretained *array;
- };
这样一来,该内存信息不在编译器内存管理对象内,仅仅是使用而已,没有对象的持有权。当然,对象所有权的持有者需要明确的管理他与该结构体的交互,不要引起不必要的错误3。
【id】与【void*】之间需要明示cast
ARC 有效的时候,由于编译器帮我们做了内存管理的工作,所以我们不需要太担心。但是当与 ARC 管理以外的对象类型交互的时候,就需要特殊的转型关键字,来决定所有权的归属问题。
主要的转型关键字是:
关键字 | 解释 |
---|---|
__bridge | 单纯的类型转换,没有进行所有权的转移 |
__bridge_retained | 类型转换是伴随所有权传递,转换前后变量都持有对象的所有权 |
__bridge_transfer | 类型转换伴随所有权转移,被转换变量将失去对象的所有权 |
当我们在 Core Foundation 对象类型与 Objective-C 对象类型之间切换的时候,需要把握下面的因素:
- 明确被转换类型是否是 ARC 管理的对象
- Core Foundation 对象类型不在 ARC 管理范畴内
- Cocoa Framework::Foundation 对象类型(即一般使用到的Objectie-C对象类型)在 ARC 的管理范畴内
- 如果不在 ARC 管理范畴内的对象,那么要清楚 release 的责任应该是谁
- 各种对象的生命周期是怎样的
题外话
Xcode
4.3带来的变化
最近随着 iOS 5.1 的推出,Xcode也推出了4.3版本。在该版本下,ARC 有效时的属性(@property) 定义的时候,如果不明确指定所有权关键字,那么缺省的就是 strong。而在 Xcode4.2 中,即使 strong 也要显示指定。
在 Xcode4.2 的时候,针对下面的代码,
- // ARC 无效
- @property (nonatomic, retain) NSString *string;
- // --->
- // ARC 有效
- @property (nonatomic, strong) NSString *string;
而在 Xcode 4.3 中,我们可以这么做,
- // ARC 无效
- @property (nonatomic, retain) NSString *string;
- // --->
- // ARC 有效
- @property (nonatomic) NSString *string;
ARC
代码自动变换
另外,Xcode 4.2开始,增加了旧代码向 ARC 代码自动转换的功能。有兴趣的朋友可以试试。位置是:
Edit->Refactor->Convert to Objective-C ARC…
为什么iOS中没有GC
我们已经知道ARC并不是GC(垃圾回收)了,那么,为什么iOS中不支持该机能呢?还特意搞出个ARC来。以下是我的分析:
- 消耗CPU时间的处理尽量避免,以节约电池电量
- GC执行的后,会停掉运行时库;这是最大的心结
- 嵌入式设备本身内存就不是很大,如果GC不停的在后台运行,执行的频率会很高,严重影响性能
- UI动画处理是iOS的一大卖点,而有了GC后可能会引起不必要的性能损失
1. 关于Objective-C对象的解释,可以参考iPhone开发入门(7)— 从C/C++语言到Objective-C语言。
2. 当然,如果你不写,编译器会用缺省的值代替。具体见iPhone开发之深入浅出 (3) — ARC之前世今生中的描述。
3. 关于这一点,可以参考iPhone开发之深入浅出 (1) — ARC是什么 一文,明白为什么 __unsafe_unretained 是危险的。
iPhone开发之深入浅出 (7) — ARC总结的更多相关文章
- iPhone开发视频教程 Objective-C部分 (51课时)
第一.二章 OC基础语法 iPhone开发教程 第一章 OC基础语法 iPhone开发概述-必看(1.1)http://www.apkbus.com/android-102215-1-1.html ...
- (转载)iPhone开发视频教程 Objective-C部分 (51课时)
感谢好人的无私贡献!来源:http://www.cnblogs.com/aimeng/p/3370012.html 第一.二章 OC基础语法 iPhone开发教程 第一章 OC基础语法 i ...
- iPhone开发视频教程 Objective-C部分
第一.二章 OC基础语法 iPhone开发教程 第一章 OC基础语法 iPhone开发概述-必看 (1.1) http://www.apkbus.com/android-102215-1-1.ht ...
- iPhone开发与cocos2d 经验谈
转CSDN jilongliang : 首先,对于一个完全没有mac开发经验,甚至从没摸过苹果系统的开发人员来说,首先就是要熟悉apple的那一套开发框架(含开发环境IDE.开发框架uikit,还有开 ...
- iOS开发UI篇—iPad和iPhone开发的比较
一.iPad简介 1.什么是iPad 一款苹果公司于2010年发布的平板电脑 定位介于苹果的智能手机iPhone和笔记本电脑产品之间 跟iPhone一样,搭载的是iOS操作系统 2.iPad的市场情况 ...
- iPad和iPhone开发区别
原文:http://mobile.51cto.com/iphone-273895.htm iPad与iPhone 开发区别详解是本文要介绍的内容,先来看看他们的区别. 1.首先我们先从官方发布的SDK ...
- 【汇总】涉及iOS&iPhone开发相关文章汇总
此文章汇总本博客中有涉及iPhone开发的相关文章,不定时更新中... 1.NSUserDefaults快速存储数据: http://www.cnblogs.com/ios-wmm/archive/2 ...
- iPhone开发常问的十个问题
iPhone开发常问的十个问题 前言 今天去stackoverflow.com上看了一下iPhone标签下排名最高的10个问题,将它们整理出来,希望这些常见问题能帮到一些iPhone开发的初学者.本来 ...
- iOS开发教程之:iPhone开发环境搭建
安装条件: 硬件:一台拥有支持虚拟技术的64位双核处理器和2GB以上内存的PC. 注意:运行MAC OS,需要电脑支持虚拟技术(VT),安装时,需要将VT启动,在BIOS中开启. 软件: Window ...
随机推荐
- [转]Expression Blend实例中文教程(8) - 动画设计快速入门StoryBoard
上一篇,介绍了Silverlight动画设计基础知识,Silverlight动画是基于时间线的,对于动画的实现,其实也就是对对象属性的修改过程. 而Silverlight动画分类两种类型,From/T ...
- hdu1693 Eat the Trees [插头DP经典例题]
想当初,我听见大佬们谈起插头DP时,觉得插头DP是个神仙的东西. 某大佬:"考场见到插头DP,直接弃疗." 现在,我终于懂了他们为什么这么说了. 因为-- 插头DP很毒瘤! 为什么 ...
- csp-s模拟测试52平均数,序列题解
题面:https://www.cnblogs.com/Juve/articles/11602244.html 平均数: 第k个平均数不好求,我们考虑二分,转化成平均数小于x的有几个 虑把序列中的每个数 ...
- C#可扩展编程之MEF(一):MEF简介及简单的Demo
在文章开始之前,首先简单介绍一下什么是MEF,MEF,全称Managed Extensibility Framework(托管可扩展框架).单从名字我们不难发现:MEF是专门致力于解决扩展性问题的 ...
- [code]彩色图像直方图均衡化 histogram_rgb
//2013.9 eageldiao #ifdef HISTOGRAM_RGB unsigned ]; unsigned intncount[]={},ncount1[]={},ncount2[]={ ...
- java内部类和静态内部类
看代码理解内部类,局部内部类和静态内部类 内部类代码: public class InnerTest { private static String name; private int age; pu ...
- UVA11916 Emoogle Grid
Emoogle Grid You have to color an M × N (1 ≤ M, N ≤ 108 ) two dimensional grid. You will be provided ...
- Leetcode137. Single Number II只出现一次的数字2
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次.找出那个只出现了一次的元素. 说明: 你的算法应该具有线性时间复杂度. 你可以不使用额外空间来实现吗? 示例 1: 输入: ...
- 用Python的requests库作接口测试——对响应进行迭代
使用 requests.Response.iter_lines() 方法,可以很方便地对流式API(例如 Twitter的流式API )的响应进行迭代. 简单地设置 stream 为 True 便可以 ...
- bzoj 4241 历史研究——分块(区间加权众数)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4241 套路:可以大力预处理,如果求区间加权众数,可以预处理i~j块(或 j 位置)的最大值, ...