详解iOS应用开发中的ARC内存管理方式

提示:本文中所说的"实例变量"即是"成员变量","局部变量"即是"本地变量"

零、简介
ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切
注意:ARC 是编译器特性,而不是 iOS 运行时特性(除了weak指针系统),它也不是类似于其它语言中的垃圾收集器。因此 ARC 和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化

一、ARC的开启和禁止
要想将非ARC的代码转换为ARC的代码,大概有2种方式:
(1).使用Xcode的自动转换工具
(2).手动设置某些文件支持ARC

1、Xcode的自动转换工具
Xcode带了一个自动转换工具,可以将旧的源代码转成ARC模式
(1).ARC是LLVM 3.0编译器的特性,而现有工程可能使用老的GCC 4.2或LLVM-GCC编译器,因此首先需要设置使用LLVM 3.0编译器:
(现使用的XCode4.5,LLVM 3.0已经升级到LLVM 4.1)

最好也选上Warnings中的Other Warning Flags 为 -Wall,这样编译器就会检查所有可能的警告,有助于我们避免潜在的问题

(2).Build Options下面的Run Static Analyzer选项也最好启用,这样每次Xcode编译项目时,都会运行静态代码分析工具来检查我们的代码

(3).设置"Objective-C Automatic Reference Counting"选项为YES,不过Xcode自动转换工具会自动设置这个选项,这里只是说明一下如何手动设置

(4).打开Xcode的自动转换工具

(5).Xcode会显示一个新窗口,让你选择哪些文件需要转换

点击Check按钮,Xcode可能会弹出对话框提示项目不能转换为ARC,需要你准备好转换(这里暂时省略详细说明)
(6).如果没有什么警告、错误了,就会弹出一下提示窗口:

(7).点击Next,几秒钟后,Xcode会提示所有文件的转换预览,显示源文件的所有改变。左边是修改后的文件,右边是原始文件。在这里你可以一个文件一个文件地查看Xcode的修改,以确保Xcode没有改错你的源文件:

点击Save即可完成转换
(8).自动转换之后,Xcode会移除所有retain、release、autorelease调用,这可能会导致代码出现其它警告、无效语法等,这些都需要自己手工进行修改
注意:Xcode的自动转换工具最好只使用一次,多次使用可能会出现比较诡异的问题。假如你第一次转换没有转换所有的文件,当你稍后试图再次转换剩余的文件时,Xcode实际上不会执行任何转换操作。因此最好一次就完成转换,没有转换的文件可以考虑手工进行修改
2、手动开启某些文件的ARC
在Compiler Flags一列加上-fobjc-arc就表示开启这个.m文件的ARC

3、禁止某些文件的ARC

在Compiler Flags一列加上-fno-objc-arc就表示禁止这个.m文件的ARC

二、原理
ARC 的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。这条规则对于实例变量、synthesize属性、局部变量都是适用的

三、strong指针
控制器中有个文本输入框框属性

代码如下:

@property (nonatomic, assign) IBOutlet UITextField *nameField;

1.如果用户在文本框中输入mj这个字符串

那么就可以说,nameField的text属性是NSString对象的指针,也就是拥有者,该对象保存了文本输入框的内容

2.如果执行了如下代码

代码如下:

NSString *name = self.nameField.text;

一个对象可以有多个拥有者,在上面代码中,name变量同样也是这个NSString对象的拥有者,也就是有两个指针指向同一个对象

3.随后用户改变了输入框的内容,比如

此时nameFeild的text属性就指向了新的NSString对象。但原来的NSString对象仍然还有一个所有者(name变量),因此会继续保留在内存中

4.当name变量获得新值,或者不再存在时(如局部变量方法返回时、实例变量对象释放时),原先的NSString对象就不再拥有任何所有者,retain计数降为0,这时对象会被释放
如,给name变量赋予一个新值

代码如下:

name = @"Jake";

我们称name和nameField.text指针为"Strong指针",因为它们能够保持对象的生命。默认所有实例变量和局部变量都是Strong指针

四、weak指针
weak型的指针变量仍然可以指向一个对象,但不属于对象的拥有者
1.执行下面的代码

代码如下:

__weak NSString *name = self.nameField.text;

name变量和nameField.text属性都指向同一个NSString对象,但name不是拥有者

2.如果文本框的内容发生变化,则原先的NSString对象就没有拥有者,会被释放,此时name变量会自动变成nil,称为空指针

weak型的指针变量自动变为nil是非常方便的,这样阻止了weak指针继续指向已释放对象,避免了野指针的产生,不然会导致非常难于寻找的Bug,空指针消除了类似的问题

3.weak指针主要用于“父-子”关系,父亲拥有一个儿子的strong指针,因此父亲是儿子的所有者;但为了阻止所有权循环,儿子需要使用weak指针指向父亲。典型例子是delegate模式,你的ViewController通过strong指针(self.view)拥有一个UITableView, UITableView的dataSource和delegate都是weak指针,指向你的ViewController

五、strong和weak指针的使用注意
1.下面代码是有问题的:

代码如下:

__weak NSString *str = [[NSString alloc] initWithFormat:@"1234"]; 
NSLog(@"%@", str); // 打印出来是"(null)"

str是个weak指针,所以NSString对象没有拥有者,在创建之后就会被立即释放。Xcode还会给出警告("Warning: Assigning retained object to weak variable; object will be released after assignment")
2.一般的指针变量默认就是strong类型的,因此一般我们对于strong变量不加__strong修饰,以下两行代码是等价的:

代码如下:

NSString *name = self.nameField.text; 
__strong NSString *name = self.nameField.text;

3.属性可以是strong或weak,写法如下

代码如下:

@property (nonatomic, strong) NSString *name; 
@property (nonatomic, weak) id delegate;

4.以下代码在ARC之前是可能会行不通的,因为在手动内存管理中,从NSArray中移除一个对象时,这个对象会发送一条release消息,可能会被立即释放。随后NSLog()打印该对象就会导致应用崩溃

代码如下:

id obj = [array objectAtIndex:0]; 
[array removeObjectAtIndex:0]; 
NSLog(@"%@", obj);

在ARC中这段代码是完全合法的,因为obj变量是一个strong指针,它成为了对象的拥有者,从NSArray中移除该对象也不会导致对象被释放

六、ARC小结
1.有了ARC,我们的代码可以清晰很多,你不再需要考虑什么时候retain或release对象。唯一需要考虑的是对象之间的关联,也就是哪个对象拥有哪个对象?
2.ARC也有一些限制:
1> 首先ARC只能工作于Objective-C对象,如果应用使用了Core Foundation或malloc()/free(),此时还是需要你来手动管理内存
2> 此外ARC还有其它一些更为严格的语言规则,以确保ARC能够正常地工作
3.虽然ARC管理了retain和release,但并不表示你完全不需要关心内存管理的问题。因为strong指针会保持对象的生命,某些情况下你仍然需要手动设置这些指针为nil,否则可能导致应用内存不足。无论何时你创建一个新对象时,都需要考虑谁拥有该对象,以及这个对象需要存活多久
4.ARC还能很好地结合C++使用,这对游戏开发是非常有帮助的。对于iOS 4,ARC有一点点限制(不支持weak指针),但也没太大关系

七、ARC使用注意总结
1.不能直接调用dealloc方法,不能调用retain,release,autorelease,retainCount方法,包括@selector(retain)的方式也不行
2.可以用dealloc方法来管理一些资源,但不能用来释放实例变量,也不能在dealloc方法里面去掉[super dealloc]方法,在ARC下父类的dealloc同样由编译器来自动完成
3.Core Foundation类型的对象仍然可以用CFRetain,CFRelease这些方法
4.不能再使用NSAllocateObject和NSDeallocateObject对象
5.不能在C结构体中使用对象指针,如果有类似功能可以创建一个Objective-C类来管理这些对象
6.在id和void*之间没有简便的转换方法,同样在Objective-C和Core Foundation类型之间的转换都需要使用编译器制定的转换函数
7.不能再使用NSAutoreleasePool对象,ARC提供了@autoreleasepool块来代替它,这样更有效率
8.不能使用内存存储区(不能再使用NSZone)
9.不能以new为开头给一个属性命名
10.声明IBOutlet时一般应当使用weak,除了对StoryBoard这样nib中间的顶层对象要用strong
11.weak相当于老版本的assign,strong相当于retain

(0)

相关推荐

  • IOS 常见内存泄漏以及解决方案

    IOS 常见内存泄漏以及解决方案 整理了几个内存泄漏的例子,由于转载地址已经找不到了,在这里就不一一列出来了. 1 OC和CF转化出现的内存警告 CFStringRef cfString = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,(CFStringRef)picDataString,NULL,CFSTR(":/?#[]@!$&'()*+,;="),kCFStringEncodingUTF8); N

  • 详解使用Xcode7的Instruments检测解决iOS内存泄露(最新)

    作为一名iOS开发攻城狮,在苹果没有出ARC(自动内存管理机制)时,我们几乎有一半的开发时间都耗费在这么管理内存上.后来苹果很人性的出了ARC,虽然在很大程度上,帮助我们开发者节省了精力和时间.但是我们在开发过程中,由于种种原因,还是会出现内存泄露的问题.内存泄露是一个很严重的问题.下面就简单介绍下怎么使用Xcode7自带的Instruments中的Leaks检测我们的程序有没有内存泄露和定位内存泄露的代码.(分析内存泄露不能把所有的内存泄露查出来,有的内存泄露是在运行时,用户操作时才产生的)

  • iOS内存错误EXC_BAD_ACCESS的解决方法

    iOS开发,最郁闷的莫过于程序毫无征兆地就崩溃了,用bt命令打出调用栈,给出的是一堆系统EXC_BAD_ACCESS的信息,根本没办法定位问题出现在哪里. 首先说一下 EXC_BAD_ACCESS 这个错误,可以这么说,90%的错误来源在于对一个已经释放的对象进行release操作.举一个简单的例子来说明吧,首先看一段Java代码: 复制代码 代码如下: public class Test{ public static void main(String[] args){ String s = "

  • 剖析iOS开发中Cocos2d-x的内存管理相关操作

    一,IOS与图片内存 在IOS上,图片会被自动缩放到2的N次方大小.比如一张1024*1025的图片,占用的内存与一张1024*2048的图片是一致的.图片占用内存大小的计算的公式是:长*宽*4.这样一张512*512 占用的内存就是 512*512*4 = 1M.其他尺寸以此类推.(ps:IOS上支持的最大尺寸为2048*2048). 二,cocos2d-x 的图片缓存 Cocos2d-x 在构造一个精灵的时候会使用spriteWithFile或者spriteWithSpriteFrameNa

  • shell脚本监控linux系统内存使用情况的方法(不使用nagios监控linux)

    一.安装linux下面的一个邮件客户端msmtp软件(类似于一个foxmail的工具) 1.下载安装: 复制代码 代码如下: # tar jxvf msmtp-1.4.16.tar.bz2# cd msmtp-1.4.16# ./configure --prefix=/usr/local/msmtp# make# make install 2.创建msmtp配置文件和日志文件(host为邮件域名,邮件用户名test,密码123456) 复制代码 代码如下: # vim ~/.msmtprcacc

  • IOS 调整内存中的图片大小实例详解

    IOS 调整内存中的图片大小实例详解 在从网路download图片,或者从相册读取图片的时候,如果ImageView的本身就是固定的300*200,那么载入2000*2000的图片是很浪费内存的. 2000*2000的内存占用是2000*2000*4bit 以下两个函数可以用来创建一个新的按照固定大小的图片.简单来说,就是Core Graphics来创建一个bitmap,然后生成一个图片. - (UIImage*)imageWithImage:(UIImage*)image scaledToSi

  • iOS通过逆向理解Block的内存模型

    前言 正常情况下,通过分析界面以及 class-dump 出来头文件就能对某个功能的实现猜个八九不离十.但是 Block 这种特殊的类型在头文件中是看不出它的声明的,一些有 Block 回调的方法名 dump 出来是类似这样的: - (void)FM_GetSubscribeList:(long long)arg1 pageSize:(long long)arg2 callBack:(CDUnknownBlockType)arg3; 因为这种回调看不到它的方法签名,我们无法知道这个 Block

  • 详解关于iOS内存管理的规则思考

    关于iOS内存管理的规则思考 自己生成的生成的对象,自己持有. 非自己生成的对象,自己也能持有. 不在需要自己持有的对象时释放. 非自己持有的对象无法释放. 注:这里的自己是对象使用的环境,理解为编程人员本身也没有错 对象操作和Objective-C方法对应 对象操作 Objectivew-C方法 生成并持有对象 alloc/copy/mutableCopy/new或以此开头的方法 持有对象 retain 释放对象 release 废弃对象 dealloc 自己生成的对象,自己持有 //自己生成

  • 解析iOS内存不足时的警告以及处理过程

    内存警告 ios下每个app可用的内存是被限制的,如果一个app使用的内存超过了这个阀值,则系统会向该app发送Memory Warning消息.收到消息后,app必须尽可能多的释放一些不必要的内存,否则OS会关闭app. 几种内存警告级别(便于理解内存警告之后的行为)  Memory warning level: 复制代码 代码如下: typedef enum {                      OSMemoryNotificationLevelAny      = -1,     

  • IOS中内存管理那些事

    Objective-C 和 Swift 语言的内存管理方式都是基于引用计数「Reference Counting」的,引用计数是一个简单而有效管理对象生命周期的方式.引用计数分为手动引用计数「ARC: AutomaticReference Counting」和自动引用计数「MRC: Manual Reference Counting」,现在都是用 ARC 了,但是我们还是很有必要了解 MRC. 1. 引用计数的原理是什么? 当我们创建一个新对象时,他的引用计数为1: 当有一个新的指针指向这个对象

随机推荐