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. 每个对象都有一个关联的整数,称为引用计数器
  2. 当代码需要使用该对象时,则将对象的引用计数加1
  3. 当代码结束使用该对象时,则将对象的引用计数减1
  4. 当引用计数的值变为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:assignunsafe_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

  1. App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()
  2. 第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
  3. 第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。
  4. autoreleasepool底层的数据结构是一个autoreleasepage的双向链表,每个page的大小为4096字节,除了存储成员变量的大小,其他的位置都用来存储autorelease对象的地址,next变量永远指向下一个可以存放autorelease对象地址的地址空间
  5. 当一个page1存储空间用完后,会创建一个新的page2,新的page2parent指针指向满了的page1page1的child指针会指向page2;
  6. 程序启动就会创建一个autoreleasepool,会调用push,程序结束时,最后会调用pop,回收所有autorelease对象的内存;
  7. 程序运行期间,同过监听runloop的休眠状态,调用push/pop方法,管理autorelease对象;
  8. 每次push的时候,会往page里面添加一个哨兵对象,这个哨兵对象作为下次pop函数的入参,遇到哨兵对象,说明这次runloop循环添加到pageautorelease对象release完毕。

以上是关于iOS开发:内存管理的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发--漫谈内存管理

iOS开发ARC内存管理技术要点

理解 iOS 的内存管理

iOS开发ARC内存管理技术要点

内存池线程池连接池,这些池子里面放的都是什么?

iOS开发进阶(iOS开发实战:理解内存管理)summary