iOS 内存管理

平时的积累记录下,记性差,方便平时查找


一 ARC和MRC的 小入门

1 MRC在ARC之前,MRC就是传统的手工内存管理方法,内存的分配和回收任务全部都落在程序员身上了。ARC是自动内存管理方法。

在iOS 5 和Xcode4.2之前,ARC还没出现,那时候使用OC的程序员在编写代码时还必须考虑内存管理的问题,并在代码中假如retain,release,dealloc等关键词来管理内存,而且必须小心谨慎,不然就会出现内存泄露和无效指针,甚至导致程序崩溃,每一个alloc,retain,copy都会有release与之对应,工作量可想而知

内存管理:顾名思义就是管理自己创建的涉及内存的指针,对象。当你新建一个对象,完成了对它的使用,你就要 自行把它从内存中释放出来,不然就造成了内存泄露,当你新建一个指针,所记录的地址的对象已经被释放,那么这就是一个无效指针,同样增加了程序的不稳定性。

在MRC中,存在着如此的对象管理规则,每一个对象被创建以后,都会有一个Reference Count,初始化为1.当你使用完时就要release掉它,使他的Reference Count 减1,当一个对象的Reference Count 为0 时,那么系统就会把它的内存释放掉

retain使Reference Count 加1 , release 使Reference Count 减1。

混合开发

ARC模式下用MRC文件

方法一:使用Xcode自带的转换功能,将MRC文件转化为ARC(容易出错)Edit—>covert —>To Object-C ARC

方法二:设置标识,告诉编译器,哪个类该用MRC模式

Build phases 选项下Compile Sources 下选择不使用arc编译的文件。双击它,输入-fno-objc-arc即可

方法三:当MRC的类比较多时,就打包成静态库(http://blog.csdn.net/qq_30970529/article/details/51076216?locationNum=14

MRC模式下用ARC文件

build phases 选项下Compile Sources 下选择要使用arc编译的文件。双击它,输入-fobjc-arc 即可

虚拟内存和物理内存:

我们的程序访问的都是逻辑地址空间(也叫虚拟地址),逻辑地址需要经过转换之后才可以访问到物理内存,

主要是两个寄存器在中间起到了强大的作用,界限寄存器用来判断是否越界,如果没有越界就会加上基址寄存器的值,转换为物理内存地址。

优先级由低到高是:IDLE(空闲)->BACKGROUND->FOREGROUND,依次类推。当内存过低的时候,就会在队列中进行广播,希望大家尽量释放内存,如果一段时间后,仍然内存不够,就会开始Kill进程,直到内存够用。

1 引用计数器:当对象没有被任何对象引用(没有指针指向该对象)时,就会被释放

使用自动释放池需要注意:

1)自动释放池实质上只是在释放的时候給池中所有对象对象发送release消息,不保证对象一定会销毁,如果自动释放池向对象发送release消息后对象的引用计数仍大于1,对象就无法销毁。

2)自动释放池中的对象会集中同一时间释放,如果操作需要生成的对象较多占用内存空间大,可以使用多个释放池来进行优化。比如在一个循环中需要创建大量的临时变量,可以创建内部的池子来降低内存占用峰值。

3)autorelease不会改变对象的引用计数

二 修饰符的介绍

基本数据类型变量:被基本数据类型修饰  实例变量:变量的类型是一个类,UIButton *, NSString * 等 ,成员变量包含:基本数据类型变量和实例变量 

属性:被property声明,自动生产set和get方法

1 property 声明时有两个选择

1 @synthesize   告诉编译器在编译期间生成set,get方法

2 @dynamic   自己实现set,get方法

2 我们在.m里面声明的变量子类是无法访问的(即使给他@public),也会被认为是@private,所以我们的对外属性都会放到.h去声明,然而由于six变量是@private,所以子类还是无法访问的

3 {

            .h文件大括号中的是成员变量,没有setget方法,不能被外部类访问, 

    }

类内使用成员变量{}, 类外使用属性@property

因此在类外的话, 强烈推荐使用属性@property. 

而如果非要在类外使用成员变量{}, 则要么将该成员变量设为@public, 要么自定义其get/set方法, 利用这两个方法从类内部对成员变量进行调用或赋值.

1)当你通过new、alloc或copy方法创建一个对象时,它的引用计数为1,当不再使用该对象时,应该向对象发送release或者autorelease消息释放对象。2)当你通过其他方法获得一个对象时,如果对象引用计数为1且被设置为autorelease,则不需要执行任何释放对象的操作;3)如果你打算取得对象所有权,就需要保留对象并在操作完成之后释放,且必须保证retain和release的次数对等。

__strong:强引用,持有所指向对象的所有权,无修饰符情况下的默认值,所有的实例变量和局部变量都是strong类型,只要该对象被strong指针指向,该对象就不会被销毁,如需强制释放,可置nil;__weak:弱引用,不持有所指向对象的所有权,引用指向的对象内存被回收之后,引用本身会置nil,避免野指针。__autoreleasing:自动释放对象的引用,一般用于传递参数

  属性的参数分为三类,基本数据类型默认为(atomic,readwrite,assign),对象类型默认为(atomic,readwrite,strong),其中第三个参数就是该属性的内存管理方式修饰,修饰词可以是以下之一  当然也可以修饰ObjC对象,但是不推荐,因为被assign修饰的对象释放后,指针还是指向释放前的内存,在后续操作中可能会导致内存问题引发崩溃。

使用set方法赋值时,实质上是会先保留新值,再释放旧值,再设置新值,避免新旧值一样时导致对象被释放的的问题。

5copy,strong,weak,assign的区别。

可变变量中,copy是重新开辟一个内存,strong,weak,assgin后三者不开辟内存,只是指针指向原来保存值的内存的位置,storng指向后会对该内存引用计数+1,而weak,assgin不会。weak,assgin会在引用保存值的内存引用计数为0的时候值为空,并且weak会将内存值设为nil,assign不会,assign在内存没有被重写前依旧可以输出,但一旦被重写将出现奔溃

不可变变量中,因为值本身不可被改变,copy没必要开辟出一块内存存放和原来内存一模一样的值,所以内存管理系统默认都是浅拷贝。其他和可变变量一样,如weak修饰的变量同样会在内存引用计数为0时变为nil。

容器本身遵守上面准则,但容器内部的每个值都是浅拷贝。

**综上所述,当创建property构造器创建变量value1的时候,使用copy,strong,weak,assign根据具体使用情况来决定。value1 = value2,如果你希望value1和value2的修改不会互相影响的就用用copy,反之用strong,weak,assign。如果你还希望原来值C(C是什么见示意图1)为nil的时候,你的变量不为nil就用strong,反之用weak和assign。weak和assign保证了不强引用某一块内存,如delegate我们就用weak表示,就是为了防止循环引用的产生。

另外,我们上面讨论的是类变量,直接创建局部变量默认是Strong修饰

6 delegate为什么要用weak或者assign而不用strong

a创建对象b,b中有C类对象c,所以a对b有一个引用,b对c有一个引用,a.b引用计数分别为1,1。当c.delegate = b的时候,实则是对b有了一个引用,如果此时c的delegate用strong修饰则会对b的值内存引用计数+1,b引用计数为2。当a的生命周期结束,随之释放对b的引用,b的引用计数变为1,导致b不能释放,b不能释放又导致b对c的引用不能释放,c引用计数还是为1,这样就造成了b和c一直留在了内存中。

而要解决这个问题就是使用weak或者assign修饰delegate,这样虽然会有c仍然会对b有一个引用,但是引用是弱引用,当a生命周期结束的时候,b的引用计数变为0,b释放后随之c的引用消失,c引用计数变为0,释放。

在MRC中使用assign修饰代理,记得在dealloc中把代理置nil,不然会和assgin一样,造成野指针

ARC中使用weak

7 深拷贝和浅拷贝

浅拷贝是创建一个新的指针指向原来的对象,引用计数器加一。深拷贝是创建一个新的指针和新的对象,让新的指针指向新的对象,新的对象引用计数器为1,老的引用计数器为1.

copy是创建一个新的对象,retain是创建一个新的指针,原有对象引用计数器加一,copy属性表示两个对象内容相同,新的对象retain为1,与旧有对象的引用计数器无关,就有对象没有变化,copy减少了对象对上下文的依赖。

retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1也即是说,retain是指针拷贝,copy是内容拷贝

[array addObject:obj];

  这样obj的引用计数会增加1,如果使用remove则obj的引用计数会减一。

  ios对集合的内存处理就是这样的。

  那么,假设obj只被array拥有:

  id temp = [array objectAtIndex:0];

  [array removeObjectAtIndex:0];

  如果你再要使用temp就会出错,因为这个时候obj已经被释放了。

NSString *str1 = @"str1";        //  copy 原则: 修改源对象的属性和行为不会对副本对象造成影响,修改副本对象对源对象也没影响,所以后面修改了str1 ,str2 不变(对于mutableCopy同样适用)

NSString *str2 = [str1 copy];    // 使用copy str1 和str2 的值和地址都是一样的

NSString *mutableCopyStr = [str1 mutableCopy];  // str1 和 str2 的值 是一样,但是地址不一样

str1 = @"haha";// 修改了str1后,str2的值不变,但是地址变了,因为:str1 = str2,都是不可变参数,值一样系统就没必要开辟新的空间, 值改变后 就会开辟新空间了

数组:

用copy修饰和赋值的变量 肯定是不可变的,(可变数组和可变字符串 用 copy 修饰和赋值 会变成不可变的)

NSArray *arr = @[@"123", @"456", @"789"];

NSMutableArray *arr1 = [arr mutableCopy];   用mutableCopy把不可变数组给可变数组赋值

   NSMutableArray *marr2 = [arr copy];    marr2  和 arr 的地址一样, 因为都为不可变数组,内容一样,没必要开辟新空间 ,arr1 和arr不一样,arr1位可变数组,重新开辟了空间

NSMutableArray *arr1 = [NSMutableArray arrayWithCapacity:1];

 NSMutableArray *marr2 = [arr1 copy];

 [marr2 addObject:@"hh"];  // 报错

NSMutableDictionary *dic1 = [NSMutableDictionary new];

    NSMutableDictionary *dic2 = [dic1 copy];

 NSLog(@"dic1:%p ------\n dic2: %p",dic1,dic2);

 NSString *TYPE1 = [dic2 fileType]; //报错,为不可变字典

 NSMutableString *tempMStr = [[NSMutableString alloc] initWithString:@"strValue"];    

 NSMutableString *tempmmmstr = nil;

 tempmmmstr = [tempMStr copy];

[tempMStr appendString:@"hhh"]; // 报错,不可变字符串不能从内部修改值,只能通过外部,或者直接赋值

可变字符串,可变数组,可变字典被相应的可变类型copy 赋值后变为 不可变数组或者不可变字典(copy 赋值的 都为不可以变的)

注意的深拷贝也仅仅是新建一个Mutable对象,而原对象如果保存有其他对 象(比如数组),那么里面的对象则是 retain 操作,Apple文档将这个称为集合的单层深拷贝。

浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制,即retain操作。

深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制,即单层深拷贝。(比如Array只深复制Array的内存地址,里面数组元素浅拷贝)

完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

集合的完全复制有两种方法:

1 调用对象本身写好的API(如initWithArray: copyItems: 和 initWithDictionary: copyItems:,最后的参数copyItems设置为YES)

执行个这种方法,集合里的每个对象都会收到 copyWithZone: 消息。这个方法要求集合里的所有对象都实现 NSCopying 协议(copy操作),如果对象没有实现 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。2

将集合进行归档(archive),然后解档(unarchive),就可以实现完全深复制,mutable对象会进行mutableCopy,而immutable对象会新建一块内存复制内容。(同样需要所有对象都实现 NSCopying、NSMutableCopying 协议)

demo地址:内存管理小demo

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

推荐阅读更多精彩内容

  • Copyright © 2017年ZaneWangWang. All rights reserved. 如果你看到...
    2897275c8a00阅读 908评论 0 1
  • 自动引用计数 什么是自动引用计数内存管理/引用计数ARC规则ARC的实现 1.1 什么是自动引用计数 ARC和MR...
    凡几多阅读 897评论 0 5
  • 为什么管理内存: 程序在运行的时候,要创建大量的对象,这些对象放在堆和栈上。(基本类型放在栈上,由系统自动管理。)...
    我是谁重要吗阅读 1,370评论 0 12
  • 软件运行时会分配和使用设备的内存资源,因此,在软件开发的过程中,需要进行内存管理,以保证高效、快速的分配内存,并且...
    maTianHong阅读 691评论 0 0
  • 冯·诺依曼体系:运算器 控制器 存储器 输入与输出 内存即存储器,用来存储指令与数据 注:哈佛体系与普林斯顿体系的...
    小李龍彪阅读 654评论 0 8