iOS开发-内存管理

内存管理

对于这篇呢,其实现在都是ARC模式,正常状态下基本不用我们去手动释放内存,所以如果不是要面试呀、装逼或者扎实功底的,就先别看了或者了解下即可,因为像面试时,有些面试官想看你的基础时,就有些人会问,现在工作基本不会用到。

学习目标

1. 掌握内存管理的原理

2. 掌握手动内存管理

===============================================

1.需要理解的知识

1.1内存管理

1.1.1 C的内存管理,以及麻烦之处

char *p = (char *)malloc(100*sizeof (char));

这是C的动态内存分配,我们手动跟系统申请了100个字节的内存;或者说系统在堆里开辟了100个字节的空间,并将这个空间的首地址返回给指针变量p。

strcpy(p,"Hello World!");

将字符串拷贝给指针变量p指向的内存空间。

puts(p);

将p指针指向的内存空间里的字符串打印出来。

free(p);

使用完成后,手动跟系统释放内存空间;或者说系统回收空间。

如上就是C里简单的内存管理。

C的内存管理,我们手动申请,手动释放。这样来看,我们只需要注意两个问题就好了:

1,申请内存,使用完成后需要释放,如果不释放会造成内存泄露。

2,不能多次释放,如果多次释放,则会崩溃。

但是,如果项目比较复杂,需要有几十上百号人一起分工完成,就很容易出现问题。

比方说我们开辟了一块内存空间,里存放了一块很有用的数据。但是,这个数据不只有我在这一块代码里用,甚至有多个人,在程序的多个地方使用。这样造成的结果就是,就算我使用完成这块内存,我也不能去释放他,因为我不能确定,别人在别的地方是否还需要使用这块内存。内存泄露在所难免了。

OC的内存管理:

1.1.2 引用计数(retainCount)

对于一块动态申请的内存,有一个人(指针)使用,就给这个内存的计数器加1,使用完成后,就给这个计数器减1,当这个内存的引用计数为0了,我们再释放他,这样,上面的问题就解决了。OC,就是使用引用计数这种方式来管理内存的。

1.1.3 内存管理的黄金法则

对于引用计数来说,有一套内存管理的黄金法则:

The basic rule to apply is everything that increases the reference counter with alloc, [mutable]copy[withZone:] or retain is in charge of the corresponding [auto]release.

如果对一个对象使用了alloc、copy、mutablecopy、retain,new,那么你必须使用

相应的release或者autorelease。

通俗一点的说法就是谁污染谁治理。

1.1.4 objective-C的内存管理遵守下面这个简单的策略:

1.你拥有你创建的对象,也就是说创建的对象(使用alloc,new,copy或者mutalbeCopy等方法)的初始引用计数是1。

2.给对象发送retain消息后,你拥有了这个对象 ,retainCount+1

3.当你不需要使用该对象时,发送release或者autorelease消息放弃这个对象

4.不要对你不拥有的对象发送“放弃”的消息

1.1.4 MRC和ARC

ARC Automatic Reference Counting,自动引用计数,由xcode,帮我们去管理内存。

MRC Manual  Reference Counting,手动引用计数,我们手动管理内存。

Xcode 5.0  版本以后默认是ARC模式,

1.1.5 如何将工程改为MRC

xcode5,工程创建的时候是ARC的,我们如果想要MRC,需要进行如下设置。

选中工程 - target - Bulid Settings -Automatic Reference Counting改为NO。

1.1.6 ARC执行了新的规则

● 开发者不能显示调用dealloc;不能实现和调用retain、release、retainCount和autorelease。

禁止使用@selector(retain),@selector(release)等等。

开发者仍可以实现dealloc方法,如果你想管理资源而不是变量。

ARC中自定义的dealloc方法,不需要调用[super dealloc](其实这样做就会导致编译错误),编译器会强制自动链接到父类。

开发者仍可以对Core Foundation-style对象,使用CFRetain,CFRelease和其他相关方法。

● 开发者不能使用NSAutoreleasePool对象。ARC下使用@autoreleasepool,它比NSAtuoreleasePool更有效率。

为了配合手动引用计数,ARC的方法命名有限制:

● 访问器方法不能已new开头,反过来就是:开发者不能声明一个已new开头的属性,除非你给你指定一个getter

// 不正确
@property NSString *newTitle;   

// 正确
@property (getter=theNewTitle) NSString *newTitle;
 

1.1.7.野指针错误形式在Xcode中通常表现为:Thread 1EXC_BAD_ACCESS(code=EXC_I386_GPFLT)错误。因为你访问了一块已经不属于你的内存。

2.需要记住的知识

2.1 alloc与release

创建一个Dog类

@interface Dog : NSObject

  @end

  @implementation Dog

  - (void)dealloc

  {

    NSLog(@"dog dealloc");

    [super dealloc];

  }

  @end

delloc里的析构函数,当对象销毁的时候,会自动调用这个方法,我们在这里重写这个方法。

在main函数里,写入如下代码:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     }

    NSLog(@"程序即将退出");

    return 0;

  }

从终端打印信息来看,程序即将退出这条打印之前,已经打印dog dealloc,也就是说在程序运行结束前,dog对象已经销毁了。这个是ARC,由xcode帮我们管理dog对象。

将ARC改为MRC,再执行程序,dog对象并没有销毁,因为我们现在是手动管理了,我们需要遵守内存管理的黄金法则,Dog *dog = [[Dog alloc] init]; 我们需要对dog进行release。将main函数代码改为如下形式:

int main(int argc, const char * argv[])

{

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     [dog release];

     }

    NSLog(@"程序即将退出");

    return 0;

}

再次执行程序,从打印可以看出,dog对象,已经销毁。这就是黄金法则,我们对dog进行alloc,就要对dog进行release。

注意,release 并不是销毁对象,让对象的引用计数减1,当对象的引用计数为0的时候,自动调用dealloc方法,销毁对象。

2.2 retain与retainCount

retain,将对象进行保留操作,也就是使对象的引用计数加1。

retainCount,打印一个对象的引用计数。

2.3 类的复合中使用

在上面代码中,增加Person类

@interface Person : NSObject {

  // 一个人,养了一条狗(持有一条狗)

    Dog *_dog;

  }

  - (void)setDog:(Dog *)dog;

  - (Dog *)dog;

  @end

  @implementation Person

  /* 版本1 (有问题) 人并没有真正持有狗,如果在main函数里[dog release],让dog的引用计数减1,就变为0,dog就销毁了。

    - (void)setDog:(Dog *)dog

  {

    _dog = dog;

  }

    */

  /* 版本2 (有问题) 如果人再持有别的狗,就会造成第一条狗得不到释放,内存泄露。

  - (void)setDog:(Dog *)dog

  {

    _dog = [dog retain];

  }

    */

  /* 版本3 (有问题) 如果本来持有一条狗,又重新设置这条狗,先进行release,这个时候,很可能dog就销毁了,然后,就没法再次retain了。

  - (void)setDog:(Dog *)dog

  {

    [_dog release];

    _dog = [dog retain];

  }

    */

  // 版本4 OK!,标准写法

  - (void)setDog:(Dog *)dog

  {

    if (_dog != dog) {

         [_dog release];

          _dog = [dog retain];

      }

  }

  - (Dog *)dog

  {

    return _dog;

  }

  - (void)dealloc

  {

    NSLog(@"person dealloc");

  // 人在销毁的时候,一并将持有的dog对象销毁

    [_dog release];

    [super dealloc];

  }

//MRC:

黄金法则:

只要使用了alloc/retain/copy/mutableCopy,new, 创建了对象

那么就必须使用release进行释放,

———总结一句话就是:谁创建,谁负责释放

retain   —  使对象的引用计数+1, 如果指针需要去持有这个对象

需要使用retain

retainCount:  返回对象的引用计数值

release :  — 使对象的引用计数 -1, 而不是释放对象

dealloc:对象销毁的时候(也就是retainCount为0的时候)自动调用这个方法

MRC:

2.4 @property retain,assign,copy展开

2.4.1 retain展开

如上代码里,Person的setter和getter方法,也可以用property,写成如下形式

@property (nonatomic, retain) Dog *dog;

则会展开如下:

- (void)setDog:(Dog *)dog

{

  if (_dog != dog)

  {

    [_dog release];

    _dog = [dog retain];

  }

}

2.4.2 assign展开

//简单数据类型 ,OC的内存管理对于简单的数据类型 int\float…,

@property (nonatomic, assign) Dog *dog;,assign是直接赋值,则会展开如下:

- (void)setDog:(QFDog *)dog

{

  _dog = dog;

}

2.4.3 copy展开  , 复制一份原来的对象

//copy 多用于字符串

@property (nonatomic, copy)NSString *name;

           展开如下:

- (void)setName:(NSString *)name

{

  if (_name != name)

  {

    [_name release];

    _name = [name copy];

  }

}

2.4 字符串内存管理

2.4.1 字符串的内存管理

// 对于字符串而言,非常不遵守黄金法则! (如果从字符串的引用计数来看,乱七八糟!) 这只是一个表象! 其实内部还是遵循的!!

// 我们要做的是,我们依旧遵守我们的黄金法则!

因此,如果是NSString,我们的property格式写成如下: @property (nonatomic, copy) NSString *name;

2.4.2 copy和mutableCopy

2.5 数组的内存管理

结论

1)当我们创建数组的时候,数组会对每个对象进行引用计数加1

2)当数组销毁的时候,数组会对每个对象进行引用计数减1

3)当我们给数组添加对象的时候,会对对象进行引用计数加1

4)当我们给数组删除对象的时候,会对对象进行引用计数减1

总之,谁污染谁治理,管好自己就可以了(数组内部也遵守内存管理)。

2.6 autorelease与autoreleasepool

在main函数里写如下代码:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

           Dog *dog = [[Dog alloc] init];

      //dog并没有马上销毁,而是延迟销毁,将dog对象的拥有权交给了autoreleasepool

      [dog autorelease];

      //这个是可以打印的,因为打印完dog的引用计数后,dog对象才销毁

      NSLog(@"retainCount = %lu",dog.retainCount);

     }

    NSLog(@"程序即将退出");

    return 0;

  }

autoreleasepool相当于一个数组,如果哪个对象发送autorelease消息,实际将对象的拥有权交给了autoreleasepool;当autoreleasepool销毁的时候,autoreleasepool里持有的对象都发送一个release消息。

2.7 加方法的内存管理

我们用加方法创建的对象,不用我们release,是因为类内部的实现使用了autorelease,延迟释放

在Dog类的声明里增加一个加方法

+ (id)dog;

在Dog类的实现里进行实现

+ (id)dog

{

注意,这里不要写成release,如果是release,那么刚创建就销毁了,使用autorelease,使得将对象的拥有权交给了自动释放池,只要自动释放池没有销毁,dog对象也就不会销毁。

return [[[Dog alloc] init] autorelease];

}

2.8  对于自动内存释放简单总结一下:

  1. 1 autorelease方法不会改变对象的引用计数器,只是将这个对象放到自动释放池中;
  2. 自动释放池实质是当自动释放池销毁后调用对象的release方法,不一定就能销毁对象(例如如果一个             对象的引用计数器>1则此时就无法销毁);
  3. 由于自动释放池最后统一销毁对象,因此如果一个操作比较占用内存(对象比较多或者对象占用资源比较多),最好不要放到自动释放池或者考虑放到多个自动释放池;
  4. ObjC中类库中的静态方法一般都不需要手动释放,内部已经调用了autorelease方法;

=====================================

ARC模式下的关键字:

__strong/__weak/__unsafe_unretain

开发者需要正确修饰变量。使用下面的格式来修饰变量声明。

类名*  修饰  变量名

例如:

       MyClass * __weak myWeakReference;
       MyClass * __unsafe_unretained myUnsafeReference; 

对应的@property 参数分别为

strong/weak/unsafe_unretain

__strong : 强引用,相当于MRC下的retain,指针对对象具有决定的占

有,默认情况。

__weak :     弱引用,指针对对象不具有决定的占有,相当于MRC下的

assign,对象释放后,指针赋值为nil。

__unsafe_unretain:弱引用,指针对对象不具有决定的占有,相当于MRC下的assign,对象释放后,指针为悬垂指针(不会赋值为nil),可以会出现野指针,不建议使用。

@property(nonatomic, strong) xxx

//set 类似于 retain 展开  [name retain]

@property(nonatomic, weak)  xxx

//类似于 assign

@property(nonatomic, unsafe_unretain) xxx

//类似于 assign

时间: 2024-09-01 13:54:31

iOS开发-内存管理的相关文章

说说iOS与内存管理(上)

http://www.cocoachina.com/ios/20150625/12234.html 说起内存管理,看似老生常谈,而真正掌握内存管理的核心其实并不简单.ARC/MRR以及“谁分配谁就负责释放”这种基本原则是很重要的,但不是本文要讨论的重点.之前本人还没在小站发过相关的文章,本篇文章中,我本人是想结合实际开发和调试中遇到的一些细节问题,来谈谈iOS的内存管理内在机制和调试方法. 上一篇文章已经是4月份的了,时间飞快又过去了好久,小站5月份没有文章更新,罪过罪过.最近小站的站长我又转换

理解 iOS 的内存管理

理解 iOS 的内存管理 远古时代的故事 那些经历过手工管理内存(MRC)时代的人们,一定对 iOS 开发中的内存管理记忆犹新.那个时候大约是 2010 年,国内 iOS 开发刚刚兴起,tinyfool 大叔的大名已经如雷贯耳,而我还是一个默默无闻的刚毕业的小子.那个时候的 iOS 开发过程是这样的: 我们先写好一段 iOS 的代码,然后屏住呼吸,开始运行它,不出所料,它崩溃了.在 MRC 时代,即使是最牛逼的 iOS 开发者,也不能保证一次性就写出完美的内存管理代码.于是,我们开始一步一步调试

总结 IOS 7 内存管理

[iOS7的一些总结]5.iOS中的内存管理 我们知道,为了更加方便地处理内存管理问题,将开发人员从繁琐的内存的分配和释放工作中解放出来而专注于产品和逻辑,iOS提供了一种有效的方法, 即自动引用计数ARC.采用了ARC之后,绝大部分内存管理的问题就不需要我们继续操心了.但是从另一个方面讲,这个方便的工具也助长了程序员的懒惰情 绪,很多新手和学生甚至连引用计数的含义都不清楚.的确,这部分内容相对比较难以理解,不过如果能深刻地领悟之后将会是未来学习和开发中的强大助推器. 1.关于对象所有权的概念:

iOS之内存管理(ARC)

iOS的内存管理,相信大家都不陌生,之前是使用的MRC,由开发人员手动来管理内存,后来使用了ARC,来由系统管理内存.本文主要讲讲Autorelease,Core Foundation对象在内存管理方面要注意的地方. Autorelease 提到内存管理,就不得不提autorelease,虽然我们平时开发中很少会感知到它的存在.autorelease就是自动释放的意思,如果变量使用autorelease来修饰,就表明变量的释放由系统来完成. autoreleasepool是由runloop在开启

iOS基础 ----- 内存管理

Objective-C 的内存管理方式有引用计数机制,垃圾回收机制,自动释放池.有alloc,就有释放.iOS应?程序出现Crash(闪退),90%的原因是因为内存问 题.在?个拥有数?个甚?是上百个类的?程?,查找内存问 题极其困难,学会内存管理,能帮我们减少出错的?率.内存问题体现在两个??:内存溢出.野指针异常. 引用计数器 在Xcode4.2及之后的版本中由于引入了ARC(Automatic Reference Counting)机制,程序编译时Xcode可以自动给你的代码添加内存释放代

iOS中内存管理

iOS中创建对象的步骤: 1,分配内存空间,存储对象 2,初始化成员变量 3,返回对象的指针地址 第一:非ARC机制: 1,对象在创建完成的同时,内部会自动创建一个引用计数器,是系统用来判断是否回收对象的唯一标示,当我们的应用计数retainCount = 0的时候,系统会回收当前对象2,[对象 release] retainCount - 1:3,[对象 retain] retailCount + 1:4,当应用计数retailCount = 0的对象就会被销毁; 5,dealloc函数,当一

【点杀ios】内存管理

1.为什要进行内存管理 ios中,每个应用程序所能占有的内存是有限制的.如果你的应用程序所占用的内存超过了这个限制,系统会给你发出内存警告.如果警告后你的程序依然继续占用过多内存,那么系统有可能强制关闭你的应用程序(闪退).并且,如果内存管理没做好,对程序的流畅度也会产生很大的影响.所以,我们在开发应用程序的过程中,要进行内存管理. 2.对什么进行管理: 内存分为堆内存和栈内存.栈内存的分配和释放是系统做的,程序员无法参与.而堆内存,是程序员进行管理的.分配和释放是程序员完成的. 任何继承了NS

iOS之内存管理浅谈

1.何为ARC ARC是automatic reference counting自动引用计数,在程序编译时自动加入retain/release.在对象被创建时retain count+1,在对象被release时count-1,当count=0时,销毁对象.程序中加入autoreleasepool对象会由系统自动加 上autorelease方法,如果该对象引用计数为0,则销毁.那么ARC是为了解决MRC手动管理内存存在的一些而诞生的. MRC下内存管理的缺点: 释放一个堆内存时,首先要确定指向这

Linux 内核开发 - 内存管理

1.1什么是内存管理 内存管理是对计算机内存进行分配和使用的技术.内存管理主要存在于多任务的操作系统中,由于内存资源极其有限,需要在不同的任务之间共享内存,内存管理的存在就是要高效.快速的非配内存,并在适当的时候回收和释放内存,以保各个任务正常的执行.最常见的内存管理机制有:段式内存管理和页式内存管理. 1.2内存中的地址 早期的16位计算中,寄存器的位宽只有16位,为了能访问到1M Bit的内存空间,CPU就采用了分段的方式来管理内存,将1M的内存分为若干个逻辑段,每个逻辑段的起始地址必须是1