在objective-c中,内存的引用计数一直是一个让人比较头疼的问题。尤其是当引用计数涉及到arc、blocks等等的时候。似乎ARC的出现只是让我们解放了双手,由于底层实现依然依赖引用计数,所以开启ARC后,只有对引用计数机制更加了解,才能避免Cycle Retain、Crash等问题的出现。

但是由于使用ARC可以显著提高编码效率,所以建议尽量启用arc,本文内容也将以arc为主,所有测试等如未说明均表示开启arc。

oc中内存的管理主要依赖引用计数,而对引用计数的影响又依赖修饰属性(暂且这么称呼),oc中常用的修饰属性如下:

属性

(1)修饰属性(使用@property定义时)

读写控制:

readwrite:可读可写,会生成getter和setter方法。

readonly:只读,只会生成getter方法,不会生成setter方法。

引用方式:

    copy:拷贝,复制一个对象并创建strong关联,引用计数为1 ,原来对象计数不变。

    assign:赋值,不涉及引用计数的变化,弱引用。ARC中对象不能使用assign,但原始类型(BOOL、int、float)仍然可以使用。

    retain:持有,对原对象引用计数加1,强引用。ARC中使用strong。

    weak:赋值(ARC),比assign多了一个功能,对象释放后把指针置为nil,避免了野指针。

    strong:持有(ARC),等同于retain。

线程安全:

    nonatomic:非原子操作,不加同步,多线程访问可提高性能,但不是线程安全的。

    atomic:原子操作,与nonatomic相反。

(2)修饰变量(修饰不使用@property定义时,比如函数内的局部变量)

    __strong:是缺省的关键词,强引用。

    __weak:声明了一个可以自动置nil的弱引用(ARC中)。

    __unsafe_unretained:声明一个弱引用,但是不会自动nil化(只有iOS 4 才应该使用)。

    __autoreleasing:用来修饰一个函数的参数,这个参数会在函数返回的时候被自动释放(类似autorelease)。

(3)默认的引用计数:

    alloc:分配对象,分配后引用计数为1。

    autorelease:对象引用计数减1,但如果为0不马上释放,等待最近一个pool时释放。

使用ARC

ARC,全称叫AutomaticReference Counting,该机制从ios5开始开始导入。简单地说,就是代码中自动加入了retain/release。所以,其底层机制还是引用计数,所以掌握引用计数对内存管理依旧非常非常重要,我甚至觉得使用arc的前提就是充分了解引用计数机制,否则几乎每天都要和Cycle Retain、Crash做斗争。

在你打开ARC时,你是不能使用retainrelease autorelease 操作的,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了,但是你需要在对象属性上使用weak 和strong, 其中strong就相当于retain属性,而weak相当于assign,基础类型还是使用assign。

(1)strong还是weak


说到底就是一个归属权的问题。小心出现循环引用导致内存无法释放,或者需要引用的对象过早被释放。大体上:IBOutlet可以为weak,NSString为copy或strong,Delegate一般为weak,基础类型用assign,不过要注意具体使用情况。

(2)outlet使用strong还是weak

官方文档建议一般outlet属性都推荐使用weak,不是直接作为main view里面一个subview直接显示出来,而是需要通过实例化创建出来的view,应该使用 strong(自己创建的自己当然要保持引用了)。但是要注意使用 weak时不要丢失对象的所有权,否则应该使用strong。

(3)delegate使用strong还是weak

delegate主要涉及到互相引用和crash(引用被释放)问题,为了防止这两个问题发生,delegate一般使用weak。先看代码:

//MyClassDelegate协议
@protocol MyClassDelegate <NSObject>
- (void)myClassOnSomeEvent:(MyClass*)myClass;
@end //MyClass类
@interface MyClass
@property (weak,nonatomic)id<MyClassDelegate> delegate; // (1)这里使用weak
@end @interface myViewController
//在myViewController中创建一个MyClass
@property (strong,nonatomic)MyClass *myClass;
@end @implementation myViewController
- (void)someAction
{
myClass = [[MyClass alloc]init]; // (2)
myClass.delegate = self; // (3)
....
}
@end

在myViewController中,

执行myClass = [[MyClassalloc] init]; //(2),此时myViewController将会持有一个MyClass的引用。

执行myClass.delegate = self;//(3)时,myClass也会引用myViewController。

1、当myClass.delegate使用weak时,

不会出现互相引用问题,也不会出现crash(引用被释放)问题,

引用关系如下(假设myViewController由Somebody持有):

上图引用关系分析:

第一行:

Somebody创建(持有)myViewController,所以myViewController的引用计数为1。

myViewController持有myClass,所以myClass的引用计数为1。

myClass通过成员delegate引用myViewController,但使用weak弱引用,所以引用计数不受影响。

第二行:

如果Somebody释放myViewController,则myViewController引用计数减1,变成0,此时myViewController自己将会被释放(引用计数是0),并减少所持有对象(myClass)的引用计数。

第三行:

myclass引用计数为0,被释放,由于是delegate是weak属性的,所以delegate将自动被设置为空。

2、当myClass.delegate使用strong时,

会出现相互引用问题,导致对象无法被释放。

引用关系如下:

第一行:

Somebody创建(持有)myViewController,所以myViewController的引用计数为1。

myViewController持有myClass,所以myClass的引用计数为1。

myClass通过成员delegate引用myViewController,但使用strong引用,所以引用计数加1,变成2。

第二行:

如果Somebody释放myViewController,则myViewController引用计数减1,变成1,此时myViewController不会被释放,myclass引用计数为1,也不会被释放,这样就造成了互相引用问题。

而且没有外部对象在引用myViewController或myclass,造成了myViewController和myclass无法被释放。

3、当myClass.delegate使用weak时,如果有另一个Somebody同时持有了myClass,

由于weak可以自动置nil,所以不会出现crash(引用被释放)问题,

则引用关系如下:

第一行:

Somebody创建(持有)myViewController,所以myViewController的引用计数为1。

myViewController持有myClass,所以myClass的引用计数为1。

myClass通过成员delegate引用myViewController,但使用weak弱引用,所以引用计数不受影响。

第二行:

新的Somebody持有同一个myClass,导致myClass引用计数加1,变成2。

第三行:

如果Somebody释放myViewController,则myViewController引用计数减1,变成0,此时myViewController自己将会被释放(引用计数是0),并减少所持有对象(myClass)的引用计数。使myClass引用计数减1,变成1。由于myClass引用myViewController的delegate是weak属性的,所以delegate将自动被设置为空,不会出现crash(引用被释放)问题。

注意,如果myClass引用myViewController的delegate是assign的话,则delegate不会被自动设置为空,将导致delegate再次调用myViewController时出错(myViewController已经释放了)。

第四行:

Somebody正常持有myClass,如果此时Somebody释放myClass,则myClass引用计数减1并释放,不会出现任何问题。

关于block和引用计数

(1)修饰block

如果需要block在它被声明的作用域被销毁后继续使用的话,你就需要做一份拷贝。拷贝会把block移到堆里面。所以,使用@property时设置通常如下:

@property(copy, nonatomic)void(^block)(void);

 

(2)retain cycle的问题

block在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在block块内使用该只读拷贝。所以在使用block过程中,经常会遇到retain cycle的问题,例如:

在block中用到了self,self会被block retain,而_observer会copy一份该block,就是说_observer间接持有self,同时当前的self也会retain_observer,最终导致self持有_observer,_observer持有self,形成retaincycle。

对于在block中的retain cycle,在2011 WWDC Session #322 (Objective-C Advancements in Depth)有一个解决方案weak-strong dance,其实现如下:

在block中使用self之前先用一个__weak变量引用self,导致block不会retain self,打破retain cycle,然后在block中使用wself之前先用__strong类型变量引用wself,以确保使用过程中不会dealloc。简而言之就是推迟对self的retain,在使用时才进行retain。

(3)return一个block

返回一个block时,ARC会自动将block加上autorelease,所以需要注意,如果执行过程中不能接受在runloop接受后才释放block,就需要自己加入@autoreleasepool块,但是测试发现64位iOS/mac时,系统会自动在使用结束后立即释放,32位则要等到runloop结束。

- (void)test
{
//@autoreleasepool{
AutoTest *a = [AutoTestsAutoTest];
NSLog(@“1”);
a = nil;
NSLog(@"2");
a = [[AutoTest alloc] init];
//}
NSLog(@"3");
}
- (IBAction)ok:(id)sender
{
[self test];
NSLog(@"4");
}

//执行结果

1释放23释放4   64位

123释放4释放   32位

12释放释放34   32位+@autoreleasepool

(4)block作为参数

block作为参数时,如果使用范围超过了block的作用域(比如异步时,或者将block传递给其他对象等等),则需要copy此block,copy建议在使用此block的方法内实现(谁使用,谁管理),而不是在传递参数时copy。注意,block过一个strong类型的指针时,会自动copy。经过copy过的block会从栈空间移动到堆上,并且,copy一个已经在堆上的block时,此block不会受影响。

*如果想了解block的内存管理原则,建议查阅相关文档,了解一下block的内部实现方式。Block的内存管理很难用简单的几句话来描述。

PDF版下载:http://download.csdn.net/detail/cuibo1123/7443125 

 

ARC的内存管理的更多相关文章

  1. 八.OC基础加强--1.autorelease的用法 2.ARC下内存管理 3.分类(category)4.block的学习

    1.autorelease的用法   1.自动释放池及autorelease介绍 (1)在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的. (2)当一个对象调用auto ...

  2. iOS 下ARC的内存管理机制

    本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...

  3. iOS 非ARC基本内存管理系列 -手把手教你ARC——iOS/Mac开发ARC入门和使用(转)

    手把手教你ARC——iOS/Mac开发ARC入门和使用 Revolution of Objective-c 本文部分实例取自iOS 5 Toturail一书中关于ARC的教程和公开内容,仅用于技术交流 ...

  4. IOS 非ARC开发内存管理的几条规则

    关于ios内存管理.在开发过程中,内存管理很重要,我简单说明一下. 1.正确用法 UIView *v = [[UIView alloc] init]; //分配后引用计数为1 [self.view a ...

  5. iOS 非ARC基本内存管理系列 2-多对象内存管理(3) 利用@property来自动管理内存

    iOS 基本内存管理-多对象内存管理(2)中可以看到涉及到对象的引用都要手动管理内存:每个对象都需要写如下代码 // 1.对要传入的"新车"对象car和目前Person类对象所拥有 ...

  6. iOS 非ARC基本内存管理系列 2-多对象内存管理(2)

    /* 多对象内存管理: 以人拥有车为例涉及到@property底层set方法管理内存的实现 注意:人在换车的时候要进行当前传入的车和人所拥有的车进行判断 */ /******************* ...

  7. iOS 非ARC基本内存管理系列 2-多对象内存管理(1)

    单个对象的内存管理非常简单无非就是alloc对应release,retain对应release.但是如果涉及到很多对象,而且对象与对象有联系的时候该怎么去管理对象的内存呢. 比如同样一本书有好3个人购 ...

  8. iOS 非ARC基本内存管理系列 1-引用计数器

    1.什么是内存管理 移动设备的内存极其有限,每个app所能占用的内存是有限制的 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间.比如回收一些不需要使用的对象.变量 ...

  9. iOS 非ARC基本内存管理系列总结6 -设计微博模型

    设计简单的微博模型:用User类和Status类来模拟实现 在非ARC机制下有两种方式,两者没有太大的区别之所以写了两种只是为了方便学习和对比两种写法! 第一种:没有使用atuorelease和自动释 ...

随机推荐

  1. BZOJ 3223 文艺平衡树 [codevs3303翻转区间]

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3223 通道2:http://codevs.cn/problem/3303/ 题目分析: 我 ...

  2. 【BZOJ】【TJOI2015】线性代数

    网络流/最小割/最大权闭合图 2333好开心,除了一开始把$500^2$算成25000……导致数组没开够RE了一发,可以算是一次AC~ 咳咳还是回归正题来说题解吧: 一拿到这道题,我就想:这是什么鬼玩 ...

  3. associated 2 maps

    <!DOCTYPE html><html> <head> <meta http-equiv="Content-Type" content= ...

  4. webvector将html转为svg或者png图片的工具

    有些js较多,html组织不好的页面转换起来很不理想,cnblog转换的还不错 http://cssbox.sourceforge.net/webvector/

  5. WaitForTargetFPS

    WaitForTargetFPS,是关于帧数限制的,你可能开了垂直同步,其实是防止撕裂.先说撕裂,在显示器的帧缓存会被不同步的显卡的帧缓存给替换掉,导致显示器显示到一半的时候,内存被换掉,你看到上频是 ...

  6. Sqli-labs less 20

    Less-20 从源代码中我们可以看到cookie从username中获得值后,当再次刷新时,会从cookie中读取username,然后进行查询. 登录成功后,我们修改cookie,再次刷新时,这时 ...

  7. Sqli-labs less 58

    Less-58 执行sql语句后,并没有返回数据库当中的数据,所以我们这里不能使用union联合注入,这里使用报错注入. Payload:http://127.0.0.1/sqli-labs/Less ...

  8. [排序] 快排 && 冒泡(自己写)

    #include <iostream> using namespace std; /* 快速排序 通过一趟排序,以轴点为界 分割为两部分:左部分 <= 轴点 <= 右部分 再分 ...

  9. 深入浅出ES6(十二):代理 Proxies

    作者 Jason Orendorff  github主页  https://github.com/jorendorff 请看这样一段代码: var obj = new Proxy({}, { get: ...

  10. mysql之触发器

    触发器     MySQL语句在需要时被执行,存储过程也是如此.但是,如果你想要某条语句(或某些语句)在事件发生时自动执行,怎么办呢?例如:每当增加一个顾客到某个数据库表时,都检查其电话号码格式是否正 ...