iOS内存管理探底

关于内存

App启动后会把程序拷贝到内存里,如下图所示,内存是一块自下而上,地址由低到高分布的区域


image.png

大致说下五大区:


  • 连续的内存区域。使用栈保护函数现场,包括函数里的局部变量的分配和释放,通过压栈和弹栈的方式保护函数现场,先进后出,由编译器自动分配释放,程序员不要管

  • 不连续的内存区域,由程序员自己分配释放([[xx alloc ] init]),又叫优先队列,本质上是二叉树

为什么要二叉树:现实中,有很多一对多的情况要处理,尤其是面向对象的框架

  • 全局区/静态区
    程序结束由系统释放
  • 常量
    常量字符串存于此(由const修饰)

常量与宏的区别:宏定义只在预处理器进行文本替换,不做任何的类型检查,大量使用还会增大二进制文件大小;常量则共享一块内存空间,就算项目中N处用到,也不会分配N块内存空间,可以被修改,在编译阶段会执行类型检查

  • 代码区
    存放二进制代码

MRC

一、引用计数(保留计数)

1.1 原理

iOS的内存管理主要是依赖引用计数,so,我们扯扯这个的工作原理:


1707017-089e69ea2340eaa6.png
- (void)fun_1{
    id obj = [[NSObject alloc] init];
    [self printCount:obj];//1
    
    [obj retain];
    [self printCount:obj];//2
    
    [obj retain];
    [self printCount:obj];//3
    
    [obj release];
    [self printCount:obj];//2
    
    [obj release];
    [self printCount:obj];//1
    
    [obj release];
    [self printCount:obj];//crash, 因为对象已经被释放,无法打印retaincount
}
- (void)printCount:(id)obj{
    printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj)));
}

注意,开启MRC模式,可以在Build phase添加-fno-objc-arc


image.png

1.2 为什么要用retaincount

在面向对象的框架中,经常会出现对象直接传递和共享数据,那此时的数据何时释放?
假如对象 A 生成了一个对象 M,需要调用对象 B 的某一个方法,将对象 M 作为参数传递过去。在没有引用计数的情况下,一般内存管理的原则是 “谁申请谁释放”,那么对象 A 就需要在对象 B 不再需要对象 M 的时候,将对象 M 销毁。但对象 B 可能只是临时用一下对象 M,也可能觉得对象 M 很重要,将它设置成自己的一个成员变量,那这种情况下,什么时候销毁对象 M 就成了一个难题。


image.png

1.3 四大原则

  • 自己生成的对象,自己持有。

  • 非自己生成的对象,自己也能持有。

  • 不在需要自己持有的对象的时候,释放。

  • 非自己持有的对象无法释放。

ARC(自动的引用计数)

ARC是苹果引入的一中自动管理内存的机制,背后的原理是依赖编译器的静态分析能力,通过在编译时找出合理的插入引用计数管理代码,让程序员放飞自我

一、修饰变量

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

1.1 __strong

__strong是默认的变量修饰符。只要还有一个强引用指向对象,该对象就一直存在。会使retaincount+1

NSObject * a = [[NSObject alloc] init];
[self printCount:a];//2

注意,这里retaincount是2不是1,跟MRC是不一样的,具体原因是因为这里等价于NSObject * __strong a = [[NSObject alloc] init];,而__strong会使retaincount+1

# 强引用对象指向强引用对象
__strong NSObject *obj1=[NSObject new];
__strong NSObject *obj2 = obj1;
obj1=nil;
NSLog(@"%@,%@",obj1,obj2);
//(null),<NSObject: 0x60000001f6c0>

1.2 __weak

__weak表示其存亡不决定所指对象的存亡,如果没有强引用指向了,就被置为nil

# 强引用对象指向弱引用对象
__strong NSObject *obj1=[NSObject new];
__weak NSObject *obj2 = obj1;
obj1=nil;
NSLog(@"%@,%@",obj1,obj2);
//(null),(null)

1.3 __unsafe_unretained

表示其存亡不决定所指对象的存亡,如果没有强引用指向了,不会被置为nil。如果它引用的对象被回收掉了,该指针就变成了野指针

1.4 __autoreleasing

用于标示使用引用传值的参数(id *),在函数返回时会被自动释放掉。

二、修饰属性

  • assign
  • weak
  • strong
  • retain
  • copy
  • unsafe_unretained
image.png
2.1 retain, strong其实一个意思
2.2 copy, strong区别
  • copy会重新开辟一块内存,并将源对象的内容传给新对象,是深拷贝
  • strong只是将源对象的指针传给新对象,是浅拷贝,如果源对象的内容变化,新对象也跟着变化
#import <Foundation/Foundation.h>

@interface XYPerson : NSObject

@property (nonatomic,copy) NSString * name;

@property (nonatomic,strong) NSString * StrongName;

@end

测试如下

NSMutableString *name = [NSMutableString stringWithFormat:@"will is so"];
self.name = name;
self.StrongName = name;
[name appendString:@" handsome"];
NSLog(@"%@ \n %@", self.name, self.StrongName);

打印如下:


image.png

很明显,StrongName内容发生了变化

拓展

经常有这样面试题
用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
答:是因为NSString、NSArray、NSDictionary有对应的可变类型NSMutableString、NSMutableArray、NSMutableDictionary,如果属性被这些可变类型赋值了,那么会导致属性无意变动,为避免这些,使用copy;如果使用strong关键字,会导致属性无意变动

2.3 strong, weak区别
  • strong:强引用,其存亡直接决定所指对象的存亡,在赋值时对对象进行retain操作,使引用计数+1
  • weak:弱引用,其存亡不决定所指对象的存亡,若所指对象被其他强引用指向,强引用置为nil,则其所指对象也置为nil
2.4 引用循环
  • delegate都是用weak修饰,为啥
    两个对象各有一个强引用指向对方,会造成引用循环
    image.png

    [tableView.delegate method]就会使得delegate引用计数+1,导致无法释放,所以用@property (nonatomic, weak) id<Delegate>delegate
  • block的引用循环,也可以用__weak
#define RCLog(obj){if (obj){printf("retain count = %ld\n",CFGetRetainCount((__bridge CFTypeRef)(obj)));}else{printf("null \n");}}

RCLog(self);//retain count = 7        
[self block:^{
    self.strongPoint = [NSDate date];
}];
RCLog(self);//retain count = 8

打印引用计数,发现+1了,这里self持有block,而block又持有self,导致了引用循环,我们用__weak来解决:

RCLog(self);//retain count = 7
__weak typeof(self) weakself=self;
[self block:^{
    weakself.strongPoint = [NSDate date];
}];
RCLog(self);//retain count = 7

有的block用__strong来修饰对象,是为了防止对象引用时,不会已经是nil了,举个例子:

RCLog(self);//retain count = 7
__weak typeof(self) weakself=self;
[self block:^{
    __strong typeof (self) strongself = weakself;
    strongself.strongPoint = [NSDate date];
}];
RCLog(self);//retain count = 7
2.5 assign, weak区别
  • assign与其他的都不一样,只有他是修饰基本数据类型和结构体的,其他的都是修饰对象的
  • weak与assign不一样的地方,是他指向的对象消失时候(内存释放),会自动置为nil,而assign则不会,这样给weak修饰的属性发送消息不会crash
    举个例子:
#import <Foundation/Foundation.h>

@interface XYPerson : NSObject

@property (nonatomic, strong) id strongPoint;
@property (nonatomic, weak) id weakPoint;
@property (nonatomic, assign) id assignPoint;

@end

测试如下:

self.strongPoint = [NSDate date];
self.weakPoint = self.strongPoint;
self.assignPoint = self.strongPoint;
    
self.strongPoint = nil;

打断点,当strongPoint置为nil后,weakPoint也置为nil,而assignPoint则出现野指针


image.png

参考
理解 iOS 的内存管理 | 唐巧的博客
Objective-C 内存管理——你需要知道的一切 - skyline75489 - SegmentFault 思否
iOS概念攻坚之路(三):内存管理 - 掘金
iOS 宏(define)与常量(const)的正确使用
iOS 内存管理总结

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

推荐阅读更多精彩内容

  • 1.1 什么是自动引用计数 概念:在 LLVM 编译器中设置 ARC(Automaitc Reference Co...
    __silhouette阅读 5,158评论 1 17
  • 那年的冬季,暂时放下了爱。不是不爱,是伤痛让自己对爱变得茫然。抓取一片枫叶,却在相思中飘零并化进了土里不留下一...
    王讲江红阅读 448评论 0 0
  • 看着窗外街灯下似曾相识的街道,听着同行的小伙伴已入梦乡的呼吸,今晚已是此行台湾的最后一夜,一周时间从南到北,真是不...
    honeyloveya阅读 180评论 0 0
  • 代表你掌握一门知识的标志是能够给别人讲述出来,如果不能讲授,说明你根本没有掌握
    MrBadman阅读 193评论 0 0
  • 新年伊始,大家伙是不是又开始进入新一轮的问候和套路之中了。累,真是累啊,千山万水总是情,少点套路行不行。这个嘛,当...
    wuli小情怀阅读 2,137评论 7 13