在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. JS 学习笔记--7---正则表达式

    正则表达式中的内容很多,也很深,下面只是一些基本的知识点,练习中使用的浏览器是IE10,若有不当处请各位朋友指正,我会在第一时间修改错误之处. 匹配的概念:是包含的意思,不是相等的意思 1.正则表达式 ...

  2. bzoj 2141 线段树套平衡树

    用树套树来解决这个问题,存储每个节点的数值是多少,然后交换 对于答案的变更可以讨论下,假设交换的是x,y位置的数x<=y 如果x=y || high[x]=high[y]则对答案没有影响 如果h ...

  3. CNAME

    CNAME指别名记录也被称为规范名字.这种记录允许您将多个名字映射到同一台计算机. 通常用于同时提供WWW和MAIL服务的计算机.例如,有一台计算机名为“host.mydomain.com”(A记录) ...

  4. Codeforces Round #283 (Div. 2)

    A:暴力弄就好,怎么方便怎么来. B:我们知道最多加10次, 然后每次加1后我们求能移动的最小值,大概O(N)的效率. #include<bits/stdc++.h> using name ...

  5. javascript 最常用的技巧整理

    1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键<table border oncontextmenu= ...

  6. web服务器 应用 服务器

    WEB服务器.应用程序服务器.HTTP服务器有何区别?IIS.Apache.Tomcat.Weblogic.WebSphere都各属于哪种服务器,这些问题困惑了很久,今天终于梳理清楚了: Web服务器 ...

  7. logback日志项目使用方法 - 150205交易模块添加日志信息logback,orderNo订单号为log主键便于跟踪,数字常量化,解决取消支付BUG,弱网络环境原因

    1.项目里面的日志,便于跟踪数据的变更和异常错误信息产生.生产环境的日志级别是INFO,测试环境日志级别DEBUG,如果生产环境的日志级别是DEBUG,虽然方便查询问题,可以看到SQL语句等信息,但是 ...

  8. 【一】php 操作符

    1.php单引号和双引号的区别 单引号和双引号都能表示字符串,但是单引号不能识别里面带有转义字符'/'和变量的字符串,所以需要""去表示这种字符串.或者使用<<< ...

  9. MetInfo安装

    安装MetInfo企业网站管理系统需要经历三个步骤:安装准备.上传文件.安装系统. 第一步:安装前的准备 环境要求:需要支持PHP并提供Mysql数据库的空间(虚拟主机),详细要求: 下载MetInf ...

  10. CAS登录后回传除了ticket参数以外的其他自定义参数

    在一次项目的技术选型中,选择了easyui+cas+shiro+spring的组合,cas实现了单点登录,这使得在一个应用中嵌入另一个应用的页面来展示数据所涉及到的授权方面变得简单. 由于shiro在 ...