iOS学习17之OC内存管理

1、内存管理的方式                                                                        

 1> iOS应用程序出现Crash(闪退),90%的原因是因为内存问题。

 2> 内存问题

  • 野指针异常:访问没有所有权的内存,如果想要安全的访问,必须确保空间还在
  • 内存泄露:空间使用完之后没有及时释放
  • 过度释放:对同一块存储空间释放多次,立刻crash
  • 内存溢出所有存储空间被占用

 3> 管理内存的三种方式

  垃圾回收机制:程序员只需要开辟存储空间,系统会自动回收内存。java采用的该机制

  MRC:手动引用计数机制,由开发人员开辟空间,手动添加影响引用计数增加减少的方法,能够灵活的控制空间何时释放

  ARC:自动引用计数机制,是iOS5.0推出的,基于MRC不需要程序员手动添加管理内存的代码,编译器会在合适的地方自动添加管理内存的代码

  MRC和ARC都是采用引用计数来管理对象内存

 4> iOS的内存管理

  iOS支持两种内存管理方式:ARCMRC

  MRC的内存管理机制是:引用计数

  ARC基于MRC的。

2、引用计数机制                                                                               

 1> 引用计数

  OC采用引用计数机制管理内存,每个对象都有一个引用计数器,用来记录当前对象的引用次数。

  iOS采用引用计数来管理内存,当你想拥有这个对象的时候,需要使该对象的引用计数+1使用完这个对象的时候,使该对象的引用计数-1,当对象的引用计数为0时,表示没有任何对象对该对象持有,那么这时候系统会自动调用dealloc方法来回收该对象的存储空间

 2> 影响引用计数的方法

  retainCount 获取对象的引用计数。(retainCount它是MRC才有的机制,所以如果使用需要将ARC改为MRC)。

  ARC改为MRC点击过程名 ---> Build Setting ---> 将Objective - C Automatic Counting 由YES改为NO 

  

  ① 使计数器加1的方法:allocretaincopy

   +alloc 开辟内存空间,让被开辟的内存空间的引用计数0变为1.

          -retain 引用计数加1,如果对象之前引用计数为1retain之后变为2,如果引用计数是5,retain之后变为6。

          -copy某一对象的内容拷贝一份,拷贝出新的对象,原有对象的引用计数不变新的对象的引用计数变1

  ② 使计数器减1的方法:release,autorelease。

          -release 引用计数立即减1,如果对象之前的引用计数为4,release之后变为3,如果之前引用计数是1,release之后变为0,内存被系统回收。

          -autorelease 未来的某一时刻引用计数减1,如果对象之前引用计数为4,autorelease之后仍然为4,未来某个时刻会变为3,这个某一时刻指的是碰到自动释放池之后才会释放。

 3> autoreleasepool的使用

  通过autoreleasepool自动释放池,控制autorelease对象的释放。向一个对象发送autorelease消息,该对象就会添加到离autorelease最近的自动释放池中,当自动释放池销毁时,为池中的每一个对象发送release消息。

  定义一个自动释放池

  第一种方式

 1         // 第一种形式
 2         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // pool 1
 3         NSLog(@"pool = %lu", pool.retainCount);
 4         
 5         Person *p1 = [[Person alloc] init];
 6         NSLog(@"p1 = %lu", p1.retainCount);
 7         
 8         [p1 retain]; // 2
 9         
10         [p1 autorelease]; // 未来的某一时刻-1
11         NSLog(@"p1 = %lu", p1.retainCount); // 2
12         
13         [pool release]; // 销毁自动释放池
14         NSLog(@"p1 = %lu", p1.retainCount); // 1

  第二种方式

 

        // 在iOS5.0之后推荐使用
        @autoreleasepool {
            Person *p5 = [[Person alloc] init];
            [p5 autorelease];
        }

 

 

  总结:自动释放池的作用:自动释放池会在销毁之前检查内部有没有autorelease对象,如果有autorelease对象,让该对象的引用计数做一次-1操作

 4> dealloc

  -dealloc 是继承自父类的方法,当对象引用计数为0的时候,由对象自动调用,销毁该对象的空间

  重写 dealloc 方法,验证对象的空间是否被回收。

1  - (void)dealloc {
2     NSLog(@"%@对象被销毁", self);
3     [super dealloc]; // 类对该方法的实现才是真正的回收空间
4 }

3、内存管理原则                                                                           

  凡是使用alloc,retain让对象的引用计数+1,相应的就该使用release或者autorelease让对象的引用计数-1,也就是说增加的次数要和减少的次数相等,才能保证对象的引用计数最终为0,对象才会被销毁。

 1> 内存泄露:增加的次数大于减少的次数

1         Person *person2 = [[Person alloc] init]; // 1
2         [person2 retain]; // 2
3         [person2 release]; // 1

 2> 过度释放:增加的次数小于减少的次数

1         Person *person1 = [[Person alloc] init];
2         [person1 retain];
3         [person1 release];
4         [person1 release];
5         [person1 release]; // 过度释放

 3> 野指针异常:增加的次数等于减少的次数,还将继续访问

1         Person *person = [[Person alloc] init];
2         [person retain];
3         [person release];
4         [person release];
5         person = nil; // 对象置为nil, 防止野指针异常
6         NSLog(@"person = %lu", person.retainCount);

4、协议                                                                                     

 1> 概述  

  Protocol(协议),是iOS开发中常用的技术。
  协议是一套标准(一堆方法的声明),只有.h文件。就像一张任务清单(或便利贴),上面写了一堆需要处理的事。清单交给谁,谁就要去完成清单上规定的任务。
  接受协议的类实现协议中定义的方法。即:清单交给谁,谁就要去完成清单上规定的任务。

 2> 协议的定义

  协议的定义:command + n —> 选择OS X下的 source —> Objective-C File —> Next —> 弹出下图。

 3> 协议的方法

  协议中的方法默认是必须实现的,即@required。关键字@optional修饰的方法是可选的,可实现也可不实现。

 1         #import <Foundation/Foundation.h>
 2         @protocol SayHello <NSObject>
 3         // 协议中的方法有两种,一种必须实现的,另一种是可选择实现的。
 4         //必须实现的方法,默认方法就是必须实现的。
 5         @required
 6         - (void)sayHello;
 7         //可选择实现的方法。
 8         @optional
 9         - (void)sayByeBye;
10         @end

 4> 协议的使用

  在类的.h文件中,在当前类父类的后面使用一对尖括号<>,遵循协议一个类可以遵循多个协议,每个协议用逗号隔开。

     @interface Person : NSObject<MarryProtocol,NSCopying>

5、拷贝                                       

 1> copy概述

  跟retain不同,一个对象想要copy,生成自己的副本,需要服从NSCopying协议,定义copy的细节(如何copy)。如果类没有接受NSCopying协议而给对象发送copy消息,会引起crash。

 2> NSCopying协议中的方法

  选中NSCopying,按住Command + 鼠标左键,进入协议查看协议中方法

@protocol NSCopying

- (id)copyWithZone:(nullable NSZone *)zone;

@end

 3> 根据copyWithZone:方法的实现不同,拷贝分为三种类型

  • 伪拷贝 : 拷贝地址,相当于retain,引用计数+1

  copyWithZone: 方法的实现

1 - (id)copyWithZone:(NSZone *)zone {
2     
3     // 伪拷贝 : 拷贝地址,相当于retain,引用计数+1
4     return [self retain];
5     
6 }
  • 浅拷贝:对象开辟新的空间,但两个对象的实例变量指向同一块存储空间

  copyWithZone: 方法的实现

 1 - (id)copyWithZone:(NSZone *)zone {
 2     
 3     // 浅拷贝:对象开辟新的空间,但两个对象的实例变量指向同一块存储空间
 4     
 5     Person *person = [[Person allocWithZone:zone] init];
 6     person.name = self.name;
 7     person.gender = self.gender;
 8     
 9     return person;
10 }
  • 深拷贝:对象开辟新的存储空间,并且两个对象的实例变量指向不同的存储空间

  copyWithZone: 方法的实现

1 - (id)copyWithZone:(NSZone *)zone {
2     
3     // 深拷贝:对象开辟新的存储空间,并且两个对象的实例变量指向不同的存储空间
4     Person *person = [[Person allocWithZone:zone] init];
5     person.name = [self.name mutableCopy];
6     person.gender = [self.gender mutableCopy];
7     return person;
8     
9 }

4> 浅拷贝与深拷贝的区别(面试题)

  如果是深拷贝,那么成员对象的地址和之前的地址不同,如果是浅拷贝,成员变量的地址和之前的地址相同。

posted @ 2016-03-29 22:35  墨隐于非  阅读(431)  评论(0编辑  收藏  举报