iOS开发:内存管理
Posted wuwuFQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发:内存管理相关的知识,希望对你有一定的参考价值。
内存的五大区域
栈区(stack)
由编译器管理(分配释放),栈从高地址向低地址扩展,是一块连续的内存区域,遵循先进后出原则,由编译器自动分配释放,存放函数参数值、局部变量的值(函数中的基本数据类型),栈区的操作方式类似于数据结构中的栈(先进后出)。栈内存分配运算效率很高,但是分配的内存量有限,比如ios中栈区的大小是2M。
堆区(heap)
由程序员管理(分配释放),堆从低地址向高地址扩展数据,采用链表形式管理内存段,就是通过new、malloc、realloc分配的内存块,根据引用计数来判断是否释放,分配方式类似于数据结构中的链表。在iOS开发中所说的“内存泄漏”说的就是堆区的内存。
全局区(又称静态区)(static)
由编译器管理(分配释放),程序结束后由系统释放。存放全局变量和静态变量。有两块区域组成全局区(静态区),一块是存放未初始化的全局变量和静态变量,另一块是初始化完成的全局变量和静态变量,这两块区域是相邻的。
常量区
由编译器管理(分配释放),在编译阶段完成分配,存放常量字符串。
代码区
存放函数体的二进制代码。
NSString* string = @"abcd";//常量string->栈
NSInteger index = 0; //index->栈
NSMutableString* mString = [[NSMutableString alloc] initWithString:@"abcd"];//mString->堆
引用计数
Objective-C提供了两种种内存管理方式:Manual Reference Counting
(MRC,手动引用计数器),Automatic Reference Counting
(ARC,自动引用计数)。ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存;
无论是手动管理内存,还是ARC机制,都是通过对 引用计数
来进行内存管理的。
- 每个对象都有一个关联的整数,称为引用计数器
- 当代码需要使用该对象时,则将对象的引用计数加1
- 当代码结束使用该对象时,则将对象的引用计数减1
- 当引用计数的值变为0时,此时对象将被释放。
- 当对象被创建(alloc、new或copy等方法)时,其引用计数初始值为1
- 给对象发送retain消息,其引用计数加1
- 给对象发送release消息,其引用计数减1
- 当对象引用计数归0时,ObjC給对象发送dealloc消息销毁对象
MRC 手动内存管理
MRC模式下,所有的对象都需要手动的添加retain、release代码来管理内存。使用MRC,需要遵守谁创建,谁回收的原则。当引用计数为0的时候,必须回收,引用计数不为0,不能回收,如果引用计数为0,但是没有回收,会造成内存泄露。如果引用计数为0,继续释放,会造成野指针。为了避免出现野指针,我们在释放的时候,会先让指针=nil。
ARC 自动内存管理
ARC是IOS5推出的新功能,通过ARC,可以自动的管理内存。在ARC模式下,只要没有强指针(强引用)指向对象,对象就会被释放。在ARC模式下,不允许使用retain、release、retainCount等方法。并且,如果使用dealloc方法时,不允许调用[super dealloc]方法。
ARC模式下的property变量修饰词为strong、weak,相当于MRC模式下的retain、assign。strong :代替retain,缺省关键词,代表强引用。weak:代替assign,声明了一个可以自动设置nil的弱引用,但是比assign多一个功能,指针指向的地址被释放之后,指针本身也会自动被释放。
属性修饰符
属性默认修饰符:atomic、readwrite、strong(引用类型)、assign(基本类型)
- strong:强引用,ARC中使用,与MRC中
retain
类似,使用之后,引用计数+1。 - retain:强引用,MRC中使用,与ARC中
strong
类似,使用之后,引用计数+1。 - weak:弱引用 ,ARC中使用,如果只想的对象被释放了,其指向
nil
,一般IBOutlet、block、delegate
里面经常会用weak
,可以有效的避免野指针,其引用计数不会增加。 - assign:
assign
和unsafe_unretained
的作用和weak
相似,区别是如果属性指向的对象被释放,指针不会被置为nil
,会出现野指针crash
。如果assign
指向的是基本数据类型如NSIntegar
,那么将有栈来管理,不会出现野指针,iOS中默认对基本类型的修饰符就是assign
。 - copy:常用于修饰有mutable子类的类型的变量,例如
NSString/NSArray/NSDictionary
,防止外面赋值是mutable数据改变以后导致里面的数据也被改了。只有实现了NSCopying
协议的对象才可以拷贝,否则会crash
。 - readwrite:同时生成getter和setter方法,属性可以读写(默认)
- readonly:只读特性,只会生成getter方法 不会生成setter方法,不希望属性在类外改变。
- atomic:原子性操作,给getter/setter加锁,多线程安全,有性能损耗。(默认)
- nonatomic:非原子操作,多线程访问可提高性能,但是线程不安全的。
AutoReleasePool
- App启动后,苹果在主线程
RunLoop
里注册了两个 Observer,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()
。 - 第一个 Observer 监视的事件是
Entry
(即将进入Loop),其回调内会调用_objc_autoreleasePoolPush()
创建自动释放池。其order
是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。 - 第二个 Observer 监视了两个事件:
BeforeWaiting
(准备进入休眠) 时调用_objc_autoreleasePoolPop()
和_objc_autoreleasePoolPush()
释放旧的池并创建新池;Exit
(即将退出Loop) 时调用_objc_autoreleasePoolPop()
来释放自动释放池。这个 Observer 的order
是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。 autoreleasepool
底层的数据结构是一个autoreleasepage
的双向链表,每个page
的大小为4096字节,除了存储成员变量的大小,其他的位置都用来存储autorelease
对象的地址,next变量永远指向下一个可以存放autorelease
对象地址的地址空间- 当一个
page1
存储空间用完后,会创建一个新的page2
,新的page2
的parent
指针指向满了的page1
,page1
的child指针会指向page2
; - 程序启动就会创建一个
autoreleasepool
,会调用push
,程序结束时,最后会调用pop,回收所有autorelease
对象的内存; - 程序运行期间,同过监听
runloop
的休眠状态,调用push/pop
方法,管理autorelease
对象; - 每次
push
的时候,会往page
里面添加一个哨兵对象,这个哨兵对象作为下次pop
函数的入参,遇到哨兵对象,说明这次runloop
循环添加到page
的autorelease
对象release
完毕。
以上是关于iOS开发:内存管理的主要内容,如果未能解决你的问题,请参考以下文章