iOS内存管理

概要

Objective-c中的内存管理,也就是引用计数。提供了两种内存管理机制MRC(Mannul Reference Counting)和ARC(Automatic Reference Counting)。

  1. MRC(手动引用计数),手动管理内存。
    MRC模式下,所有的对象都需要手动的添加retain、release代码 来管理内存。使用MRC,需 要遵守谁创建,谁回收的原则。也就是谁alloc,谁release;谁retain,谁release。当引用计数为0的时候,必须回收,引用计数不为0,不能回收,如果引用计数为0,但是没有回 收,会造成内存泄露。如果引用计数为0,继续释放,会造成野指针。为了避免出现野指针,我们在释放的时候,会先让指针置nil。
  2. ARC(自动引用计数),自动管理内存。
    ARC是IOS5推出的新功能,通过ARC,可以自动的管理内存。在ARC模式下,只要没有强指针(强引用)指向对象,对象就会被释放。在ARC模式下,不允许使用retain、release、 retainCount等方法。并且,如果使用dealloc方法时,不允许调用[super dealloc]方法。

1.引用计数

看到“引用计数”大家很容易想到“某处有某物多少个”,而Objc采用引用计数的思想是:

  1. 当对象被创建(通过alloc、new、copy、MutableCopy等方法)时,其引用计数初始值为1。
  2. 被其他变量引用而持有时,其引用计数加1。
  3. 其他变量使用结束而释放时,其引用计数减1。
  4. 当引用计数的值变为0时,表示对象没有被任何代码使用,此时对象将被释放。
    如果1-1所示:


    引用计数的内存管理

示例:

@implementation Test

- (instancetype)init {
    if (self = [super init]) {
        NSLog(@"对象被创建,初始引用计数为%ld", self.retainCount);
    }
    return self;
}

- (void)dealloc {
    NSLog(@"对象被销毁");
    [super dealloc];
}

@end

在main.m中创建Test对象

    NSObject *obj = [[Test alloc]init];
    
    NSObject *obj1 = [obj retain];
    NSLog(@"对象进行retain操作,引用计数:%ld",obj.retainCount);
    
    [obj1 release];
    NSLog(@"retain对象进行release操作,引用计数:%ld",obj.retainCount);
    
    [obj release];

输出结果为:

2017-11-07 20:24:48.047429+0800 MemoryManger[15557:2996092] 对象被创建,初始引用计数为1
2017-11-07 20:24:48.047605+0800 MemoryManger[15557:2996092] 对象进行retain操作,引用计数:2
2017-11-07 20:24:48.047780+0800 MemoryManger[15557:2996092] retain对象进行release操作,引用计数:1
2017-11-07 20:24:48.047989+0800 MemoryManger[15557:2996092] 对象被销毁

注:调用或释放已经销毁的对象时会导致程序奔溃


15101041233195.jpg

2. autoreleasePool

自动释放池顾名思义,就是一个池子,可以容纳对象,并且自动释放。为什么需要自动释放池了?因为当一个对象不再使用时应该将其释放,但是什么时间释放?这个时间点很难确定。Objc提供autorelease,当一个对象接收到autorelease消息时,它会被注册到当前的自动释放池,当自动释放池被销毁时,会给池子里的所有对象发送release消息将其释放,但在这个时间段内,对象可以正常使用。

它与release的区别如图1-2所示:


autorelease与release的区别

在Cocoa框架中,相当于程序主循环的NSRunLoop或者在其他程序可运行的地方,对NSAutoreleasePool对象进行生成,持有和废弃处理。如果1-3所示:


15101072086365.jpg

注:

  1. autorelease不会改变对象的引用计数
  2. 自动释放池实质上只是在销毁的时候给池中的所有对象发送release消息,并不能保证对象一定被释放,如果接受release消息的对象的引用计数仍大于1,对象就无法释放
  3. 自动释放池中的对象会集中同一时间释放,如果操作需要生成的对象较多占用内存空间大,会产生内存不足的现象。典型的例子就是读入大量图像的同时改变其尺寸。图像文件读入到NSData对象,并从中生成UIImageview对象,改变对象尺寸后生成新的UIImageview对象。这种情况下就会产生大量autorelease对象,可以创建内部的池子来降低内存占用峰值。

3. MRC内存管理基本原则

简单的说就是:谁创建,谁释放;谁retain,谁release

  1. 当通过alloc,new,copy,MutableCopy方法创建一个对象时,它的引用计数为1,当不再使用该对象时,应该向对象发送release消息或autorelease消息释放对象。
  2. 如果获得对象所有权,就需要保留对象并在操作完成之后释放,保证retain和release成对出现。

4. ARC

4.1 所有权修饰符

ARC有4中修饰符: __strong, __weak, __autoreleasing, __unsafe_unretained(为兼容iOS5以下版本的产物,可以理解成MRC下的weak)

  1. __strong:表示对对象的“强引用”,无修饰符情况下的默认值。
  2. __weak:弱引用,不持有所指向对象的所有权.
    注:
    1). 互相强引用,很容易引起循环引用问题。__weak修饰符的变量不持有对象,所以在超出其变量作用域时,对象即被释放。故在可能发生循环引用的类成员变量改用__weak修饰,即可避免。如图所示:



    __weak修饰符避免循环引用

2). 弱引用指向的对象被释放后,引用本身会置nil,避免野指针。

    id __weak obj0 = nil;
    {
        id  obj1 = [[NSObject alloc]init]; // obj1变量为强引用
        obj0 = obj1;                       // objc0变量持有对象的弱引用
        NSLog(@"A:%@",obj0);               // 输出objc0变量持有的弱引用的对象
    }
    /*
     * 1. objc1变量超出其作用域,强引用失效,自动释放自己持有的对象
     * 2. 对象无持有者,对象被销毁
     * 3. 持有改对象的弱引用obj0变量的弱引用失效,赋值为nil
     */
    NSLog(@"B:%@",obj0);

输出结果:

2017-11-08 13:52:01.008357+0800 MemoryManger[19017:3772013] A:<NSObject: 0x600000010070>
2017-11-08 13:52:01.008508+0800 MemoryManger[19017:3772013] B:(null)

3). __autoreleasing:自动释放对象的引用,一般用于传递参数。
比如解析数据方法:

+ (nullable id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;

调用时会发现如图提示

编译器会将源代码转化成以下形式:

 NSError *error = nil;
 NSError * __autoreleasing tmp = error;
 id value = [NSJSONSerialization JSONObjectWithData:self options:kNilOptions error:&tmp];

5. 属性的内存管理

属性的修饰词可以分为三类:

  1. 原子性:nonatomic,atomic
  • nonatomic:非原子性访问,不加同步,多线程并发访问会提高性能。
  • atomic:为原子操作,是防止在写未完成的时候被另外一个线程读取,造成数据 错误。这种机制是耗费系统资源。
  1. 读写属性:readwrite,readonly
  • readwrite:是默认属性,生成getter和setter方法
  • readonly:只生成getter方法
  1. 内存属性:assign、strong/retain 、weak、copy
  • assign:直接赋值,索引计数不改变,适用于基本数据类型
  • strong/retain:release旧值,retain新值(用于OC对象)
    使用set方法赋值时,实质上是会先保留新值,再释放旧值,再设置新值,避免新旧值一样时导致对象被释放的的问题。
MRC写法:
- (void)setTitle:(NSString *)title {
        [title retain];
        [_title release];
    _title = title;
}
ARC写法:
- (void)setTitle:(NSString *)title {
    _title = title;
}

  • copy : release旧值,copy新值。一般用来修饰String、Dict、Array等需要保护其封装性的对象,尤其是在其内容可变的情况下,因此会拷贝(深拷贝)一份内容給属性使用,避免可能造成的对源内容进行改动。

  • weak:对对象的一种弱引用,不保留新值,也不释放旧值,只设置新值。


希望本文能抛砖引玉,帮助大家对内存管理有更多的理解。如有不当之处,希望大家能多多指点我。


References:
《Objective-C高级编程》

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

推荐阅读更多精彩内容