内存管理基本原则

内存管理的依循下面的基本原则
  • 自己生成的对象,那么既是其持有者
  • 不是自己生成的对象,也可成为其持有者(一个对象可以被多个人持有)
  • 如果不想持有对象的时候,必须释放其所有权
  • 不能释放已不再持有所有权的对象

    不管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中就需要像下面一样处理内存的释放。

1
2
3
4
- (void) dealloc
{
free(buffer_);
}

又或者是注册的delegate对象,观察者对象需要被删除的时候,也是在dealloc函数中动作。

1
2
3
4
- (void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

如果在ARC无效的时候,我们还要像下面一样,调用父类对象的dealloc函数。

1
2
3
4
- (void) dealloc
{
[super dealloc];
}

但是当ARC有效的时候,[super dealloc];的调用已经被编译器自动执行,已经不需要我们明示调用了。如果你在代码中还这样写,难免遇到下面的错误。

    error: ARC forbids explicit message send of ’dealloc’
[super dealloc];
^ ~~~~~~~

内存管理相关的函数必须遵循命名规则

我们知道如果是 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 文件的时候,往往像下面这样写。

1
2
3
4
5
6
    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。

1
2
3
4
5
6
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的对象时,将出现类似下面的编译错误。

1
2
3
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 关键字。比如下面:

1
2
3
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 管理范畴内,CFBridgingRetain/CFBridgingRelease
    • 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 的时候,针对下面的代码,

1
2
3
4
5
6
7
// ARC 无效
@property (nonatomic, retain) NSString *string; // ---> // ARC 有效
@property (nonatomic, strong) NSString *string;

而在 Xcode 4.3 中,我们可以这么做,

1
2
3
4
5
6
7
// 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后可能会引起不必要的性能损失

ARC使用小结的更多相关文章

  1. block使用小结、在arc中使用block、如何防止循环引用

    引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题: [小测试]你真的知道blocks在Objective-C中是怎么工作的吗 ...

  2. ARC 和 MRC 小结

    ARC 和 MRC 内存管理 从 MRC—>ARC 就是将内存管理部分,从开发者的函数中转移到函数外部的runtime 中.由于 runtime 的开发简单,逻辑层次高,所以 runtime 的 ...

  3. iOS--->微信支付小结

    iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...

  4. iOS GCD 编程小结

    一.简单介绍 1.GCD简介? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD优势 GCD是苹果公司为多核的并行运算提出的 ...

  5. iOS ARC内存管理

    iOS的内存管理机制,只要是iOS开发者,不管多长的时间经验,都能说出来一点,但是要深入的理解.还是不简单的.随着ARC(自动管理内存)的流行.iOS开发者告别了手动管理内存的复杂工作.但是自动管理内 ...

  6. iOS静态库小结--(yoowei)

    准备知识: 1.什么是库? 库是程序代码的集合,是共享程序代码的一种方式 2.根据源代码的公开情况,库可以分为2种类型 a.开源库 公开源代码,能看到具体实现 ,比如SDWebImage.AFNetw ...

  7. iOS开发——OC基础-ARC、BLOCK、协议

    一.ARC ARC 是一种编译器特性!而不是IOS运行时特性,和JAVA中得垃圾回收机制完全不一样ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的 ...

  8. 关于@property()的那些属性及ARC简介

    @property()常用的属性有:nonatomic,atomic,assign,retain,strong,weak,copy. 其中atomic和nonatomic用来决定编译器生成的gette ...

  9. ARC指南1 - strong和weak指针

      一.简介 ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain.release.autorelease语句.你不再需要担心内存管理,因 ...

随机推荐

  1. php登录

    if ($name && $passowrd){ $sql = "SELECT * FROM liuyanban WHERE name = '$name' and passw ...

  2. 关于iOS自定义控件:在view上实现事件和代理

    自定义控件.h #import <UIKit/UIKit.h> #import "PPViewtouchesBeginDelegate.h" @interface PP ...

  3. NSString之Format

    三点用法总结: 类型转换:基本类型到对象类型 需要调用NSString的fotmat方法 NSString  *location = [NSString  stringWithFormat:@&quo ...

  4. CodeForces 660B Seating On Bus

    模拟. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #inc ...

  5. Laravel Cache 使用

    在项目中使用 laravel 的 cache 时,使用下面形式方法: $value = Cache::remember('users', $minutes, function() { return D ...

  6. (简单) POJ 1426 Find The Multiple,BFS+同余。

    Description Given a positive integer n, write a program to find out a nonzero multiple m of n whose ...

  7. [iOS Animation]-CALayer 显示方式

    寄宿图 图片胜过千言万语,界面抵得上千图片 ——Ben Shneiderman 我们在第一章『图层树』中介绍了CALayer类并创建了一个简单的有蓝色背景的图层.背景颜色还好啦,但是如果它仅仅是展现了 ...

  8. ZOJ 3923 Handshakes

    水题. 算一下每个人和之前的人握手次数+之后的人和这个人握手次数.取最大值. #include<cstdio> #include<cstring> #include<cm ...

  9. CSS3样式linear-gradient的使用(切角效果)

    linear-gradient linear-gradient是CSS3中新增的样式,主要用于颜色的渐变效果.MDN地址 linear-gradient在不同内核下使用方式不同,详细内容可参考w3cp ...

  10. 对float的理解

    从IE6下的双边距引出 对一个div设置float:left;同时设置了margin-left:100px时在IE6下会出现双边距. 有两种解决办法: 1,推荐办法.加display:inline 2 ...