内存管理是开发软件中重要的一个课题。如果内存管理不当,轻者内存泄露,重者程序崩溃。

下面重要讲述一下iOS的ARC(Automatic Reference Counting))机制。

ARC的历史由来

在iOS1.0的时候,苹果公司没有开放手机开发接口,不存在iOS开发者这个概念。

在iOS2.0的时候,我们管理内存使用的技术数MRC机制。

在iOS5.0的时候,苹果公司推出了ARC机制。

我们知道在MRC管理内存的时候,我们需要遵守“谁创建,谁释放,谁引用,谁管理”这个黄金法则,我们在创建对象的时候,需要考虑在什么时候释放内存,有时候对内存管理机制不熟悉的话,就很有可能导致内存泄露和过度释放的问题。

手动管理内存会导致很多问题的产生,在iOS5.0的时候,苹果推出了自动内存管理ARC,当时使用的IDE是XCode4.0,在我们创建工程的时候,下面有一个选项,是否使用ARC,给开发者一个自主选择,但是推出ARC,去很少人去使用ARC,在XCode5.0的时候,已经不存在这个选项,我们创建工程默认选择的是ARC机制,我们我们想要使用MRC,需要手动配置工程。

在推出ARC的时候,很多人都说iOS的ARC和Android的GC机制很像,但是他们本身还是有很大的区别的。其中GCS是运行时特性,ARC是编译时特性。

ARC的使用

ARC我们字面翻译是自动引用计数,引申意我们可以理解为自动内存管理。自动内存管理难道我们真的不需要管理了吗?非也。ARC机制也会导致内存泄露的问题,我们在使用的时候,需要注意这些问题。

我们在MRC中,我们经常要使用release,autorelease,retain这些关键字,来保留引用计数或者释放对象。在ARC中,我们就不能这么处理了。如果我们使用这些关键字,我们的程序在基本的编译都不能通过。

既然我们创建的对象,不用我们手动释放,系统在必要的时候会为我们释放,那么对象会在什么时候释放掉呢?

探讨这个问题之前,我们先说明一下使用ARC的基本准则。

  • 强引用指向的对象不会被释放。
  • 一个对象没有强引用会立刻释放。
  • 弱引用指向的对象将要释放时自动为空。

注意:我们创建的对象默认是强引用,比如:People = [People new];等价__strong People = [People new];

下面我们讲述的内容都围绕着这三个准则。

一、局部对象

首先创建一个工程,然后添加一个类People,下面为People的.m文件内容。

 @implementation People
-(id)init
{
if (self = [super init])
{
NSLog(@"%s",__FUNCTION__);
}
return self;
}
-(void)dealloc
{
NSLog(@"%s",__FUNCTION__);
}
@end

People.m

我们在viewDidLoad中添加People *p = [People new]; NSLog(@"%s",__FUNCTION__);

打印结果为:

2015-10-19 13:16:10.589 textarc[2627:93432] -[People init]
2015-10-19 13:16:10.600 textarc[2627:93432] -[ViewController viewDidLoad]
2015-10-19 13:16:10.600 textarc[2627:93432] -[People dealloc]

我们发现People这个对象别释放,从打印顺序上我们可以看到是在viewDidLoad执行完毕之后这个People对象被释放掉的。

局部对象为什么会在函数执行完毕之后被释放掉呢?

在viewDidLoad中,p是一个强引用,对象不会被释放,会打印NSLog中的内容。但是在函数执行完毕之后,强引用指针不在指向对象。根据上面的准则,没有强指针指向对象会被立刻释放,所以在执行完viewDidLoad之后,对象People会被释放掉。

如果我们在viewDidLoad中这样写:

__weak People *p = [People new];
NSLog(@"%s",__FUNCTION__);

我们可以猜测到打印结果就会跟上面的不同

2015-10-19 13:22:10.783 textarc[2682:96983] -[People init]
2015-10-19 13:22:10.783 textarc[2682:96983] -[People dealloc]
2015-10-19 13:22:10.784 textarc[2682:96983] -[ViewController viewDidLoad]

因为对象没有强指针引用,所以People会被释放,然后执行下面的打印。

二、全局变量

现在我们将People定义成全局对象,命名为_people。

我们在viewDidLoad中写下面代码:

_people = [People new];
 NSLog(@"%s",__FUNCTION__);

打印结果为:

2015-10-19 13:28:32.076 textarc[2764:100790] -[People init]
2015-10-19 13:28:32.077 textarc[2764:100790] -[ViewController viewDidLoad]

我们发现并没有调用People的dealloc方法,因为在执行完viewDid这个函数时,还有一个强引用指针指向People,根据上面的准则,对象不会被释放。

如果我们在全局对象前面加上一个__weak:   __weak People *_people;

再执行上面的代码,打印结果为:

2015-10-19 13:31:47.816 textarc[2803:102843] -[People init]
2015-10-19 13:31:47.827 textarc[2803:102843] -[People dealloc]
2015-10-19 13:31:47.828 textarc[2803:102843] -[ViewController viewDidLoad]

出现这个结果不用解释了吧,虽然是一个全局的对象,但是是弱引用,没有强引用,对象会被释放掉。

三、全局和局部混合使用

现在我们定义一个全局行强指针People的对象p,同时创建一个局部性强指针对象p1.

viewDidLoad中为:

People *p1 = [People new];
p = p1;

执行结果为:

2015-10-19 13:49:32.380 textarc[3088:111876] -[People init]
2015-10-19 13:49:32.383 textarc[3088:111876] -[ViewController viewDidLoad]

因为p1是局部强引用,在函数执行完后按常理是被释放掉,但是在释放前,有一个全局性的强引用执行了它,所有People没有被释放掉。

如果我们在viewDid中这样写:

__weak People *p1 = [People new];
p = p1;

NSLog(@"%s",__FUNCTION__);

打印结果为:

2015-10-19 13:50:43.647 textarc[3115:112734] -[People init]
2015-10-19 13:50:43.651 textarc[3115:112734] -[People dealloc]
2015-10-19 13:50:43.651 textarc[3115:112734] -[ViewController viewDidLoad]

因为局部对象是弱引用,对象会被释放掉,在赋值给全局强引用之前,它已经为空了,对象已经释放掉了。

四、创建对个对象嵌套

假设我们还有一个类,类名为Car,People有一个属性,@property(nonatomic,strong) Car *car;

现在我们把People声明一个全局对象p.

在viewDidLoad中:

p = [People new];
Car *c = [Car new];
p.car = c;
NSLog(@"%s",__FUNCTION__);

打印结果:

2015-10-19 13:57:21.883 textarc[3220:116572] -[People init]
2015-10-19 13:57:21.886 textarc[3220:116572] -[Car init]
2015-10-19 13:57:21.886 textarc[3220:116572] -[ViewController viewDidLoad]

Car虽然是局部引用,但是函数执行完毕后没有释放,因为p对其还有一个强引用,所有Car不会被释放掉。

如果我们将People释放掉,Car自然也会释放。

我们想要释放一个一个变量的话,我们可以直接将变量赋值为空,就会释放掉。

例如:

p = [People new];
Car *c = [Car new];
p.car = c;
p = nil;
NSLog(@"%s",__FUNCTION__);

打印结果为:

2015-10-19 14:01:14.703 textarc[3265:118993] -[People init]
2015-10-19 14:01:14.706 textarc[3265:118993] -[Car init]
2015-10-19 14:01:14.706 textarc[3265:118993] -[People dealloc]
2015-10-19 14:01:14.706 textarc[3265:118993] -[ViewController viewDidLoad]
2015-10-19 14:01:14.707 textarc[3265:118993] -[Car dealloc]

五、循环引用问题

加入在People中有Car这个属性,在Car中有People这个属性。(注意交叉引用问题)

我们在viewDidLoad中:

People * p = [People new];
Car *c = [Car new];
p.car = c;
c.people = p;
NSLog(@"%s",__FUNCTION__);

结果为:

2015-10-19 14:05:50.296 textarc[3320:121202] -[People init]
2015-10-19 14:05:50.298 textarc[3320:121202] -[Car init]
2015-10-19 14:05:50.298 textarc[3320:121202] -[ViewController viewDidLoad]

我们发现都没有释放,会导致内存泄露。当然我们可以将c,或者p赋值为nil将其释放掉。但是这样写很糟糕,我们需要判断什么释放决定释放。

如何解决这个问题呢?

在属性修饰的时候,不要同时使用strong,可以一个使用weak修饰,则运行结果如下:

2015-10-19 15:45:53.289 textarc[3937:150043] -[People init]
2015-10-19 15:45:53.298 textarc[3937:150043] -[Car init]
2015-10-19 15:45:53.298 textarc[3937:150043] -[ViewController viewDidLoad]
2015-10-19 15:45:53.298 textarc[3937:150043] -[People dealloc]
2015-10-19 15:45:53.298 textarc[3937:150043] -[Car dealloc]

 ARC总结
在使用ARC的时候,可以在一定程度上简化我们的编程操作,但是在使用的过程中也会出现内存泄露的问题,需要我们在实际使用过程中总结出现问题的情况,让我们的程序有更少的bug和潜在的bug。
 
 
 
 
 
 

iOS-ARC机制的更多相关文章

  1. [转]iOS ARC机制 weak strong

    写在开头 虽然距离WWDC2011和iOS 5已经快一年时间,但是很多开发者并没有利用新方法来提高自己的水平,这点在ARC的使用上非常明显(特别是国内,基本很少见到同行转向ARC).我曾经询问过一些同 ...

  2. iOS开发ARC机制下的内存管理技术要点

    转载一篇: iOS开发ARC内存管理技术要点.ARC内存管理原则总结.iOS ARC内存管理总结 ARC内存管理机制 (一)ARC的判断准则: 只要没有任何一个强指针指向该对象,该对象就会被释放. ( ...

  3. 黑马程序员——ARC机制总结和用ARC建立模型

    ARC 全称:Automatic Reference Counting 使用ARC 只需要在建立一个新的项目的时候把 下面的√打上 Xcode5以后都会默认建议开发者使用ARC机制 新的项目中如果有部 ...

  4. IOS 消息机制(NSNotificationCenter)

    消息机制 NSNotificationCenter 一直都在频繁使用,但是却对其原理不是十分了解.今天就花些时间,把消息机制原理重头到尾好好过一遍. iOS 提供了一种 "同步的" ...

  5. iOS ARC基本原理

    一.ARC基本简介 ARC:Automatic Reference Counting 自动引用 完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain.release.autor ...

  6. ARC机制集合内存管理

    // //  main.m //  13-ARC机制集合内存管理 // //  Created by apple on 14-3-21. //  Copyright (c) 2014年 apple. ...

  7. ARC机制下组合关系

    // //  Person.h //  01-autorelease基本概念 // //  Created by apple on 14-3-18. //  Copyright (c) 2014年 a ...

  8. iOS事件机制(二)

    从上一篇的内容我们知道,在iOS中一个事件用一个UIEvent对象表示,UITouch用来表示一次对屏幕的操作动作,由多个UITouch对象构成了一个UIEvent对象.另外,UIResponder是 ...

  9. iOS 事件处理机制与图像渲染过程(转)

    iOS 事件处理机制与图像渲染过程 iOS RunLoop都干了什么 iOS 为什么必须在主线程中操作UI 事件响应 CALayer CADisplayLink 和 NSTimer iOS 渲染过程 ...

  10. iOS 事件处理机制与图像渲染过程

    Peter在开发公众号功能时触发了一个bug,导致群发错误.对此我们深表歉意,并果断开除了Peter.以下交回给正文时间: iOS 事件处理机制与图像渲染过程 iOS RunLoop都干了什么 iOS ...

随机推荐

  1. Beta冲刺(周四)

    这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1 这个作业要求在哪里 https://edu.cnblo ...

  2. Write Once, Run Anywhere:这不是Java,这是C#

    注意,本文目的并非挑起语言之争.虽然有为C#平反之意,但主要还是介绍Mono并进行简单的测试. UPDATED: 25th August 2012 更新了「Compile Once, Run Anyw ...

  3. guruguru

    6576: guruguru 时间限制: 1 Sec  内存限制: 128 MB提交: 28  解决: 12[提交] [状态] [讨论版] [命题人:admin] 题目描述 Snuke is buyi ...

  4. Java中this关键字的用法

    this关键字作用: 1. 如果存在同名成员变量与局部变量时,在方法内部默认是访问局部变量的数据,可以通过this关键字指定访问成员变量的数据. 2. 在一个构造函数中可以调用另外一个构造函数初始化对 ...

  5. 解决Windows 与Mac 双系统下的蓝牙设备共用的问题

    不知道有多少人和我一样用的蓝牙鼠标或者键盘,有的话应该都会遇到同一个问题:即在一个系统下配好对后在另一个系统必须重新配对才能使用,很是麻烦.还要将蓝牙设备进入发现模式,OS下搜索,连接....终于昨天 ...

  6. css3 设置滚动条的样式

    ::-webkit-scrollbar { width: 14px; height: 14px; } ::-webkit-scrollbar-track, ::-webkit-scrollbar-th ...

  7. 三倍经验——bzoj3663、4660、4206 Crazy Rabbit/最大团

    题目描述: 3663 4660 4206 题解: 第一眼:不成立的互相连边,然后用网络流求解无向图最小点覆盖! 好吧我不会. 正解: 每个点对应圆上的一段圆弧,长这样: 设对应圆弧$(l,r)$. 若 ...

  8. python爬虫: 豆瓣电影top250数据分析

    转载博客 https://segmentfault.com/a/1190000005920679 根据自己的环境修改并配置mysql数据库 系统:Mac OS X 10.11 python 2.7 m ...

  9. init0-6(启动级别)

    一. init是Linux系统操作中不可缺少的程序之一. 所谓的init进程,它是一个由内核启动的用户级进程. 内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后, ...

  10. vue实现与安卓、IOS交互

    方案背景 IOS用的是jsBridge插件实现调用.传参.回调的 安卓是在window挂载方法和挂载回调的 IOS实现方案 调用原生方法封装如下 function setupWebViewJavasc ...