华山论剑之iOS内存,内存管理,copy(拷贝)

我想大部分人都知道通常一个程序员会具有的美德。当然了,有三种:懒惰,暴躁,傲慢。 ----Perl语言发明者Larry Wall

我想不管是iOS的,还是Java的初学者,内存算得上心中的一个永远抹不去的痛吧,当时作为初学者的我也是一度苦恼,不知道该如何理解这个内存,随着不断的学习,自己对内存也有更深的了解.



内存

说到内存,不能不说一下内存的分区,内存总共分为五大区,分别是栈区 堆区 静态区 常量区 代码区,五个区是按照内存地址从大到小分配的.
内存的分区示意图
各个分区的的特点
其实我们最常用的还是栈区和堆区的内存,然后首先来看一下栈区内存的特点吧,

1.栈区内存

 1.局部变量的存储空间基本都是栈区,局部变量在函数,循环,分支中定义
 
 2.在栈区的存储空间由高向低分配,从低向高存储.!!
 
 3.栈区内存由系统负责分配和回收,程序员开发者没有管理权限.
 
 4.当函数,循环,分支执行结束后,局部变量的生命周期就结束了.之后不能再进行使用,由系统销毁
 
 5.栈底,栈顶:栈底是栈区内存的起始位置,先定义的变量所占用的内存,从栈底开始分配,后定义的变量所占用的内存,逐渐向栈顶分配.
 
 6.入栈,出栈:入栈,定义新的局部变量,分配存储空间.出栈,局部变量被销毁,存储空间被收回.
 
 7.栈的特点:先进后出,后进先出.例如:子弹夹添加子弹,打出子弹.

2.堆区内存

 1.由开发者负责分配和回收.
 
 2.忘记回收会造成泄漏.
 
 3.程序运行结束后,需要及时回收堆内存,但是如果不能及时回收堆内存程序运行期间可能会因为内存泄漏造成堆内存被全部使用,导致程序无法使用.

3.常量区内存

1.常量存储在常量区,例如:常量数字,常量字符串,常量字符,

2.常量区存储空间由系统分配和回收

3.程序运行结束后,常量区的存储空间被回收

4.常量区的数据只能读取,不能修改,修改的话会造成崩溃.

4.静态区内存

 1.全局变量,使用static修饰的局部变量,都存储在静态区.
 
 2.静态区的存储空间由系统分配和回收.
 
 3.程序运行结束后,静态区的存储空间被回收,静态变量的生命周期和程序一样长.
 
 4.静态变量只能初始化一次,在编译时进行初始化,运行时可以修改值
 
 5.静态变量如果没有设置初始值,默认为0.
5.代码区内存
 1.由系统分配和回收
 
 2.程序运行结束之后,由系统回收分配过的内存存储空间

内存管理

上面我们说到了内存的五大区,现在我从内存的管理来说一下内存,为什么我们开发应用的时候要要注意内存呢?由于移动设备的内存有限,所以每一个APP应用程序的内存也是有限的,App所占用的内存较多时,系统就会发出内存警告.为了节省内存的使用.所以我们就要使用到内存管理,就是当对象不再被使用的时候,我们要对其内存及时的进行回收.
内存管理这一块我要说的是我们在MRC(Manual Reference Counting)环境下需要手动管理堆区的内存,进行release操作等,但是在 ARC(Automatic Reference Counting)环境下,我们不需要手动管理我们的内存.首先我们需要看一下如何切换ARC环境和MRC环境.

首选我们需要打开工程的配置面板,然后Build setting面板中搜索auto(自动)关键的字样,然后就可以查找到ARC环境的切换选项了

ARC环境切换示意图
下面我就说一下内存管理的核心,内存管理的核心就是引用计数的加减,引用计数相当于对象的一个属性,当然了,这个属性是不需要我们手动创建的,系统会帮每一个对象进行创建的.
引用计数示意图
引用计数器的作用:用来判断对象是否应该回收内存空间,当引用计数器为0时,此时需要回收对象的内存空间.
引用计数器的操作:
retain 引用计数器 +1
release 引用计数器 -1
retainCount 得到引用计数器的值
如果一个对象被释放的时候,引用计数为0了,就会使用一个方法,析构函数dealloc;
然后,我们就看一下内存管理的黄金法则,

内存黄金法则:

谁alloc,谁release!(包括new);

谁retain,谁release!

(retain) 引用计数+1

(release) 引用计数-1

谁copy,谁release!


野指针与内存泄露

说到内存管理就不得不说一下内存泄露和野指针问题.
内存泄漏

用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,不能被任何程序再次使用,直到程序结束。即所谓内存泄漏。
注意:内存泄漏是指堆内存的泄漏。
 简单的说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。

野指针

“野指针”不是NULL指针,是未初始化或未清零的指针,他指向的内存地址不是程序员想要的。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。野指针的成因主要有两种:
  一、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
  二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。

#pragma mark---研究简单的野指针---
        Person *p = [Person new];
        
        //使用KVC对属性进行赋值
        [p setValue:@"栋栋" forKey:@"name"];
        [p  retain];

        //调用方法
        [p eat];
  
        //释放
        [p release];

        //此处是野指针,在正常的情况下是可以访问的.如果想要查找野指针,就要打开僵尸模式,editscheme->diagnostics->zombie
        [p eat];

        [p release];//如果不release就会产生内存泄漏.

上面说到了僵尸模式,然后我们就看看如何开启僵尸模式,检测野指针的存在.

开启僵尸模式


拷贝

对于拷贝,也是让大多数人头疼的,因为拷贝烤着烤着就把自己给弄糊涂了,所以,今天最后我还要说一下这个copy相关的问题,首先拷贝分为可变拷贝和不可变的拷贝.来看一下实际的范例吧

NSString * p1 =@"Jobs";
        
        NSString *p2 = [NSString stringWithFormat:@"wang"];
        
        NSString *p3 = [[NSString alloc]initWithString:p1];
        
        NSString *p4 = [NSString stringWithString:p1];
        
        NSLog(@"%@ ----- %p",p1,p1);
        
        NSLog(@"%@ ----- %p",p2,p2);
        
        NSLog(@"%@ ----- %p",p3,p3);
        
        NSLog(@"%@ ----- %p",p4,p4);

        NSLog(@"%p",p1);

然后打印结果是这样的..


打印结果

这样我们不难发现,p3和p4只是指针的重指向而已.

不可变拷贝

下面就是不可变拷贝,当我们这样使用的时候 程序是会crash掉的,虽然p2的类型是可变的字符串对象,由于p2是p1进行不可变出来的,其实相当于p2指向了p1,通过地址我们不难发现p1和p2指针指向的是相同的一个地址.

        NSString * p1 =@"Jobs";

        NSLog(@"%p",p1);
        
        //可变字符串对象  指向   不可变拷贝的数据
        NSMutableString * p2 = [p1 copy];
        
        NSLog(@"%p",p2);
        
        //如果上面使用的是 copy,那么此处就会crash,如果使用的是,mutableCopy那么将会改变
        
        [p2 appendString:@"steve"];
运行的结果

</br>

可变拷贝

如果我们将上面的过程换成可变的拷贝,然后再进行字符串的拼接会有什么情况的产生呢?

        NSString * p1 =@"Jobs";

        NSLog(@"%p",p1);
        
        //可变字符串对象  指向   不可变拷贝的数据
        NSMutableString * p2 = [p1 mutableCopy];
        
        NSLog(@"%p",p2);
        
        //如果上面使用的是 copy,那么此处就会crash,如果使用的是,mutableCopy那么将会改变
        
        [p2 appendString:@"steve"];
可变拷贝的打印结果

通过上面的两个实例,我们简单的对可变拷贝和不可变拷贝做一下总结,用于以后的工作中

上述总结:

copy(不可变拷贝),如果使用了不可变拷贝,那么接收的对象不管是可变对象还是不可变的对象,都不能改变拷贝过来的内容

mutableCopy(可变拷贝),如果使用了可变拷贝,那么接收对象不管是可变的还是不可变的对象,都可以改变拷贝过来的内容


浅拷贝和深拷贝

对于浅拷贝和深拷贝,其实就是拷贝对象的区别,浅拷贝拷贝的是地址,而深拷贝拷贝的是对象的本身.

#pragma mark---怎么判断深拷贝----
        
        //通过两个对象的地址来判断,如果地址不同,那么就是深拷贝.
        
#pragma mark---怎么判断浅拷贝----
        
        //通过两个对象的地址来判断,如果地址相同,那么就是浅拷贝.
        

这里我给大家附加上一道面试题,来提高大家对copy的理解

#pragma mark--- 面试题 ---
        
        //1.你怎么样理解深拷贝和浅拷贝.
        //浅拷贝:就如同人和影子,在内存,人没影子就没了,影子没了人就没了.就是操作的同一个空间.
        //深拷贝:就如同人和克隆,在内存中,人没了克隆还在,克隆没有,人还在.就是操作的不同的空间.
        
        //2.如何对深拷贝和浅拷贝进行内存释放
        //浅拷贝:释放一个即可.因为释放哪个对象,都是同一个空间.
        //深拷贝:释放全部,因为是两个对象,而且是两个空间.




今天我就先说到这,后期将说一下,对象属性的实现原理,如果您喜欢这篇文章就点个 '喜欢' 吧,不要打赏,只求 ' 喜欢 ',谢谢各位看官了.


参考博客原文:http://blog.csdn.net/dangercheng/article/details/12618161

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

推荐阅读更多精彩内容

  • 内存管理 简述OC中内存管理机制。与retain配对使用的方法是dealloc还是release,为什么?需要与a...
    丶逐渐阅读 1,867评论 1 16
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,876评论 0 7
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 1,982评论 0 12
  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,204评论 0 6
  • iOS内存管理 概述 什么是内存管理 应用程序内存管理是在程序运行时分配内存(比如创建一个对象,会增加内存占用)与...
    蚊香酱阅读 5,631评论 8 119