iOS内存管理机制及属性的使用

对于OC的使用者来说,最会被问到的就是iOS开发中的内存管理。而只要涉及到内存管理,就肯定会涉及到property。而且在平常iOS开发的面试中,我们也经常会被问到相关的问题。所以这次就结合我所见到的和自己对于属性的理解来进行简述,希望对你们有帮助。

在讲述属性前,我们需要对于iOS开发中的内存管理有一个初步的了解。

本文中的部分内容来源于:这里这里还有部分来源于苹果官方文档。

iOS开发中的内存使用情况

  • 栈(stack):栈是编译器自动分配并释放,用来存放函数的参数,局部变量。
  • 堆(heap):堆一般是程序员自己分配和释放,如果我们在使用的过程中,没有释放,那么等到程序完全结束,系统将会对堆中的内容进行回收。一般开发中的alloc就是存放在堆中的
  • 全局变量(静态变量)(static):全局变量和静态变量是单独存放的,因为他们的声明周期和整个程序的生命周期一致。释放的时间是由整个程序结束后系统负责回收。
  • 文字常量区:一般用来存放常量字符串,比如说String *str = @"hello world",他的释放也和全局变量一致,在程序结束后进行释放。
  • 程序代码区:用来存放函数的二进制内容的区域。

他们之间的在内存中存放的关系如下图所示:

内存管理

而在日常开发过程中,我们所遇到最多的也是最容易问到的就是,可能有的人对于这两者还是很模糊,所以我再引用之前看到过的一段话来解释(由于很久以前看到了,已经忘记了出处,如果有人能够告诉我,我一定加上):

内管管理就像做菜,而栈(stack)的使用就像我们下馆子,我们只要去酒店,告诉老板我们要什么,他们就会帮我们准备好,我们只要吃完付钱,就可以了,也不用去管理那些之后的琐事。而堆(heap)就像我们自己做菜,什么东西都得自己买好,做什么,怎么做都得提前想好。做完之后还需要自己收拾最后的垃圾。一般我们alloc一个对象,该对象的内存就会被分配到堆上,而非对象创建的时候就会放到栈上,由系统在不需要的时候进行回收。

iOS内存管理机制

当你创建的对象的时候,你总得需要告诉操作系统,你将要在什么时候对占有这块内存的对象进行释放,就像平常生活中,你怎么才能确定这房间里面是有人的还是没有人的呢?

一般来说,我们肯定会这么干,每个人进去的时候登记下,然后出来的时候再登记下,这样只要我们在需要管理的时候看下登记再按的人数是不是为0就可以了,只要为0,那么就说明里面没有人,否则就有人,苹果也是这么处理内存管理的。在你创建一个对象的时候,他会需要进行retain,然后在你不在持有他的时候进行release,所以每个对象都有一个retain count来进行计数。

在iOS中有2套内存管理机制:MRC(MannulReference Counting)和ARC(Automatic Reference Counting)。其中ARC起源于iOS 4.3,在那之前,苹果开发者只能手动使用retainrelease来进行内存管理。这样做的问题很明显,如果你在开发过程中,一个不小心没有retainrelease成对出现,那么很容易使得内存没有释放,最后导致程序内存不足而导致闪退。

对于ARC,苹果的官方文档是这么解释的:

Automatic Reference Counting (ARC) is a compiler feature that provides automatic memory management of Objective-C objects. Rather than having to think about retain and release operations, ARC allows you to concentrate on the interesting code, the object graphs, and the relationships between objects in your application.

ARC works by adding code at compile time to ensure that objects live as long as necessary, but no longer. Conceptually, it follows the same memory management conventions as manual reference counting (described in Advanced Memory Management Programming Guide) by adding the appropriate memory management calls for you.

总的来说,主要意思就是ARC在本质上就像MRC一样,但是它能够让你花费更少的时间来考虑代码中的retainrelease。与此同时,编译器能够帮你更加准确的将retainrelease加在你代码中真正需要的地方。

理解如下图:


ARC && MRC
ARC && MRC

有人可能会提到GC(Garbage Collection)——垃圾回收机制。在现在的iOS中GC没有被使用,而在MAC OS X中,GC是被使用的,不过对于GC,苹果是这么解释的:

Garbage collection is deprecated in OS X Mountain Lion v10.8, and will be removed in a future version of OS X. Automatic Reference Counting is the recommended replacement technology. To aid in migrating existing applications, the ARC migration tool in Xcode 4.3 and later supports migration of garbage collected OS X applications to ARC

总的来说ARC将在未来的某一天来取代GC在MAC中的位置。

property(属性)

就算理解的再多,我们还是要和实际相结合。实际运用中与之相对应的就是实例变量的属性。
由于 iOS 中 MRC已经太过于古老,在这就不再多提。

在ARC中,我们创建实例变量经常是这样的

@property (atomic/nonatomic/assign/retain/strong/weak/unsafe_unretained/copy) Number* num

所以我们就为让这内容中的这几个property来进行解释。

  • atomic:原子性,简单的解释就是说,他是线程安全的,但是由于线程安全,在操作的过程中,编译器会自己给他上锁,解锁。这样会造成资源的浪费(因为我们平常开发中不会那么频繁的考虑到线程安全这个问题)该属性为默认值
  • nonatomic:非原子性,这个和上面那个是双子星,但是他们正好相反,这个由于不安全的,但是因为没有了锁的问题,这样资源就会尽可能的被利用,所以我们在日常开发中使用这个,而对于他的安全性,我们一般都自己在后期开发的过程中,在使用多线程的过程中进行考虑。
  • assign:给予了setter方法,assign一般指向一个不是指针指向的对象,比如说CGFloat这类值。(关于assignweak在delegate中的区别将会在后面提到)
  • retain:retain用于指向一个有指针的对象,就像MRC中的retain,他是为了增加retain count,这样使得对象能够在autoreleasepool中持有内存
  • strong:strong是在ARC中用来代替retain的,所以原理相同,即retain count加一
  • weak:weakstrong一般只要懂一点英语的就知道,他们又是一堆双子星,strong是表示持有,而weak表示,只是简单的引用。这意味着等到持有weak的对象呗释放的时候,weak表示的对象也被释放。而需要注意的是weak释放后他就会为nil。
  • unsafe_unretained:unsafe_unretained一般很少被用到,主要的原因是他一般用于在Cocoa底层的那些不能支持weak属性的变量,比如说NSTextViewNSFontNSColorSpace等。而他和weak不同的地方是,持有weak的对象被释放后,weak对象会被指向nil,而unsafe_unretained则不会被指向为nil。这样就有了安全隐患。
  • copy:copy一般我们用在NSStringNSArrayNSDictionary,而原因就是copy会在复制的时候讲源对象进行拷贝。这样他只想的将会是一个新的,retain count为1的对象。这样在我们对于这个对象的内容进行修改的时候,它将不会影响到原来的那个对象。

除了这几种之外,我们还有4种属性没有提到

  • getter:设置getter方法
  • setter:设置setter方法
  • readonly:只读
  • readwrite:可读写

这四种因为可以根据字面意思来进行设置在这就不进行过多的介绍。

这里再补上一句之前提到的问题:

关于为什么使用weak而不是用assign来对delegate进行标注。

首先delegate一般的类型都是id,即可以指向所有对象的id类型。所以我们既可以使用assign指向他,也可以使用weak指向他。但是因为weak在不被持有的时候会指向nil,而大家都知道,所有通过nil的方法在调用函数的时候,都会返回为nil,这样就能够保证了程序的稳定性,就算没有东西返回,他还是能够正常解析(只是解析出来的值为nil)。而如果使用assign的话,如果一不持有他。那么下次再调用它的时候,他将会指向一个不知名的地址,即野指针。这样就会使得整个程序crash。

autoreleasepool(自动释放池)

既然扯到release,那就不得不提下autoreleasepool,顾名思义,他就是一个用来自动帮你释放的池子(这特么不是废话么)。一般情况下我们不怎么会去使用它,因为在AppKit和UIKit的框架中,事情基本上默认的放在autorelease pool block中完成的。这样子当你完成这些内容后,对应在内存中的数据就会自动释放,这样就不需要你手动去处理这些数据,从而保证了程序的安全性。

当然这里也提到了这是在使用AppKit和UIKit的情况下。我们仍然有以下这几种情况来使用autoreleasepool的情况:

  1. 编写的是命令行程序,不基于UI框架
  2. 当你需要写一个循环,循环里面有很多临时变量的时候
  3. 当你大量使用辅助线程

总的来说autoreleasepool是为了尽可能的减少无用变量在内存中的占用情况。从而使得程序所需要的内存更少。至于autoreleasepool的释放时间,这就要涉及到runloop,具体内容可以参见sunnyxx大神的这篇文章,相信对你肯定有很大帮助。

循环引用

循环引用对于内存来说是一个大问题。这个问题我们更多的在block中可能会碰到,不过总结起来就是这么一句话:

A对象持有B,那么B的retain count 为1,而此时A因为被其他变量持有,所有A的retain Count为1,而B又要引用A,那么A的retain count又要加一,这样等到持有A的那个对象释放A的时候,就算A的retain count减一,但还是为1,所以无法释放,而因为A无法释放,导致B也无法成功释放,而外部没有持有A或B中的任意一个,这样就导致了这块内存空间一直被持有。

图示如下:


循环引用1

循环引用2

具体的问题我将会在解释block的时候具体说明,需要知道的一点就是,如果存在循环引用,那么就需要把循环引用中间的一条线断掉,从而使用weak来代替,使得当strong的一方被释放的时候,weak也能被正常释放。

希望以上文章能够对你有所帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容