iOS 内存管理

iOS的操作系统运行的简单原理

当前的主流iPhone实际物理内存一般都不超过1G,要高效利用的这1G空间,达到高性能,ios是怎么做的嗯?iOS的操作系统更是抛弃了不必要的复杂——系统层面不支持App内存页换出,当内存吃紧时,对于可以重新载入的只读数据来说,直接清理掉,而对于可写的数据,只能通过App自己去管理维护。内存紧张时,iOS会向App发起memory warning,不配合释放足够内存者,kill掉!

进程的内存结构

最简单来说分为两大部分:指令+数据。再细分一点,五部分:代码(指令),初始化数据区,未初始化数据区,堆,栈。
** 主要的布局如下图所示:**

内存结构.png

** 从代码上来分析:**

//main.cpp 
int a = 0; 全局初始化区 
char *p1; 全局未初始化区 
main() 
{ 
int b; 栈 
char s[] = "abc"; 栈 
char *p2; 栈 
char *p3 = "123456"; 123456\0在常量区,p3在栈上。 
static int c =0; 全局(静态)初始化区 
p1 = (char *)malloc(10); 
p2 = (char *)malloc(20);  分配得来得10和20字节的区域就在堆区。 
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 
} 
  • 栈区(stack):由编译器自动分配和释放,存放 函数的参数值、局部变量值等,栈是向低地址扩展的数据结构,是一块连续的内存区域。即栈顶的地址和栈的最大容量是系统预先设定好的。

  • 堆(heap):一般由程序员分配和释放、若程序员不释放,程序结束后由OS回收。堆是不连续的内存区域,向高地址扩展的数据结构。

  • 区别:堆只有动态分配的,栈有静态分配(编译器完成,比如局部变量)和动态分配的(alloc函数,编译器来释放);栈的分配效率比堆高,但堆灵活和内存占用大,由于堆空间的不连续性,会存在碎片问题。

  • 全局区和静态区:全局变量和静态变量是放在一块的,内部划分初始化和未初始化两个块区。程序结束后由系统释放。

  • 文字常量区:常量字符串放在这里的,程序结束后由系统释放

  • 程序代码区:存放函数体的二进制代码

iOS的内存管理机制

** MRC(iOS 5.0 之前)**

MRC,手动引用计数, 手动管理内存的三个原则

  1. 谁创建,谁释放,如果你通过alloc new copy来创建一个对象,那么你必须调用release或者这autorrelease。换句话说,,不是你创建的,你就不用去释放。

  2. 除了alloc、new、copy之外的方法创建的对象都被声明了autorelease。

  3. 谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release

** ARC(iOS 5.0 之后)**

自 iOS5.0 之后,苹果开始支持ARC(Automatic Reference Counting)自动引用计数,是一个编译器特性,编译器自动的添加完成了内存管理的引用计数代码。arc只针对OC对象类型,OC类型:任何继承于NSObject对象都属于OC的类型。它对基本数据类型不管用。

** @property属性 **

OC中@property属性关键字的使用(assign/weak/strong/copy) ,property的属性默认是:readwrite、strong、 atomic(没有这个关键字)

  • assign
    用于 ‘基本数据类型’、‘枚举’、‘结构体’ 等非OC对象类型
    eg:int、bool ,struct,enum等

  • copy
    一般用在NSString*类型、block类型上

  • strong
    OC对象类型(NSArray、NSDate、NSNumber、模型类)
    一个对象只要有强指针引用着,就不会被销毁
  • weak
    一般应用: IBOutlet UI控件、delegate代理(防止循环引用)
    weak型的指针变量指向的对象不存在时,会自动置为nil。

注: 如果还能打印出指针的地址,说明指针并不会马上销毁,那么什么时候回销毁恩?
在每一个运行循环(runloop)的开始,系统会自动创建一个自动释放池autoreleasepoo;在每一个运行循环的结尾,系统会自动销毁这个自动释放池。
当一个自动释放池自身被销毁(dealloc)时,它会给池中每一个对象发送一个release消息,所以进行下一轮消息循环(runLoop)之后才会发现这个指针被销毁了。

注意:使用arc机制后代码中不能再出现这些:

  1. 代码中不能使用retain,release,autorelease
  2. 不重载dealloc(可以重写覆盖,不能调用)
  3. 不能使用NSAutoReleasePool、而需要@autoreleasepool块

IOS 内存泄漏及预防

在使用ARC的时候,可以在一定程度上简化我们的编程操作,但是在使用的过程中也会出现内存泄露的问题

  • delegate循环引用造成的内存泄露
    把delegate引用声明成了strong
@property (nonatomic, strong) id delegate;

解决办法:改成弱引用

@property (nonatomic, weak) id delegate; 或者  @property (nonatomic, assign) id delegate;
  • block的循环引用
    Block 循环引用也很常见,假设 self 代表当前 ViewController,那么一般写法是这样
  __weak typeof(self)weakSelf = self;
  self.completionHandler = ^{
    NSLog(@"%@", weakSelf);
};
  • NSTimer
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

如果在一个 ViewController 里创建了一个定时器,并且 repeats:值为 YES,一定记得在 pop/dismiss 当前 ViewController 将 timer 设为 invalidate,否则 会造成循环引用,这里要特别需要 注意 的一点是:我们不要在 ViewController 的 dealloc 方法里调用 [timer invalidate]; 因为从来不会调到这里,我们应该在 viewWillDisappear里边调用,像这样:

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    if ([self isMovingFromParentViewController]) {
        [timer invalidate]; //解除对self的引用
    }
}
  • CoreFundation/CoreGraphics 相关函数
    ARC 针对的 Objective-C 对象来说的,而 CoreFundation/CoreGraphics 这种非Objective-C对象,我们一定要记得自己手动释放,否则会造成内存泄漏
- (void)display {
   dispatch_async(backgroundQueue, ^{
       CGContextRef ctx = CGBitmapContextCreate(...);
       // draw in context...
       CGImageRef img = CGBitmapContextCreateImage(ctx);
       CFRelease(ctx);
       dispatch_async(mainQueue, ^{
           layer.contents = img;
       });
   });
}

只要记住一点:CG/CF 开头的函数,有创建就要有释放,这样可以有效降低内存泄漏的风险。

  • 基本数据类型申请的内存空间需要手动free
    uint8_t *buffer=malloc(1024)
    free(buffer)

如何检测内存泄漏

开发应用程序,内存泄漏不可避免,那么如何检测内存泄漏呢,那就要说道 Xcode工具套件中的 Instruments。针对 内存泄漏,主要是两个 Allocations 和 Leaks

这里有篇教程写的比较详细:http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
我建议大家直接看英文原版吧,也不难。这里主要说明几点:

教程中用的 Instruments 界面跟目前最新的略微有些差别,比如 里边 提到的处于左边的 Mark Heap 按钮,目前改为了 Mark Generation ,在这里:


ed
ed

对于 循环引用 如何进行检测
简单点可以通过在对应的类中复写 dealloc 方法,像这样:

- (void)dealloc{
    NSLog(@"=== %@ dealloced! ===", NSStringFromClass([self class]));
}

如果调用这里,说明不存在循环引用。

同样,我们也可以通过 Instruments 来检测,利用 Allocations ,如图:


ee
ee

这里以 ViewController 为例,在搜索框中输入想要查看的类名,然后连续进行 pop/push 操作,看 #Total 那一列的个数有没有连续增加,如果连续增加,那么说明出现循环引用了,注意:此方法只适用于真机。

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

推荐阅读更多精彩内容

  • Copyright © 2017年ZaneWangWang. All rights reserved. 如果你看到...
    2897275c8a00阅读 861评论 0 1
  • Objective-C,顾名思义,是一门超C的语言,自从ARC(Auto Reference Count)出现了之...
    xiao彰阅读 450评论 0 2
  • 冯·诺依曼体系:运算器 控制器 存储器 输入与输出 内存即存储器,用来存储指令与数据 注:哈佛体系与普林斯顿体系的...
    小李龍彪阅读 638评论 0 8
  • ARC ARC是 Object-C 编译特性, 不是运行时特性也不是垃圾回收机制, ARC 所做的只是在代码编译自...
  • # 前言 反复地复习iOS基础知识和原理,打磨知识体系是非常重要的,本篇就是重新温习iOS的内存管理。 内存管理是...
    Vein_阅读 724评论 0 2