Objective-C内存管理

1.内存管理中的基本问题

  1.1为什么要进行内存管理    分配在堆空间中的对象 需要手动去释放

回顾堆栈的区别                                                                          生命周期 

栈空间    函数 函数中局部变量                   调用函数压栈    函数调用结束 释放  

数据段    静态变量  全局变量                      程序开始             程序结束 释放

堆:                malloc  alloc                                程序猿手动释放 free() release

 

  1.2内存管理的定义  

内存管理就是确保开辟的堆空间被正确的释放。

内存管理中的问题:

1.内存泄露  堆空间没有释放

2. 内存崩溃 野指针 (过早释放:使用已经释放的空间     

重复释放:重复释放同一个空间)

 

  1.3 C语言内存管理的缺陷

1 释放一个堆 ,必须保证所有使用堆的指针结束使用,避免(提前释放);

2  释放一个指针,确保指向同一个堆的指针,只有一个被释放,避免(重复释放);

3 模块化分工编程,不易明确谁来释放

4 多线程操作,不能确定哪个线程最后结束

 

  1.4 OC内存管理的基本原则

1.对象在完成创建的同时,内部会自动创建一个引用计数器,这个计数器,是系统用来判断是否回收对象的唯一依据,当我们的引用计数retainCount = 0的时候,系统会毫不犹豫回收当前对象

 2.[对象 retain]   reatinCount + 1 ,返回self

 3.[对象 release]  reatinCount - 1

 4.我们的引用计数retainCount = 0时  对象就被销毁了

  5.dealloc函数,当一个对象要被销毁的时候,系统会自动调用dealloc函数,通知对象你将要被销毁

  内存管理原则(配对原则):只要出现了 new,alloc,retain,就一定配对出现一个release,autorelease

 1         Person * p = [[Person alloc] init];
 2         NSLog(@"%lu",p.retainCount);
 3 //        [p retain];
 4         Person * q = [p retain];
 5         NSLog(@"%lu",q.retainCount);
 6         
 7         [p release];
 8         NSLog(@"%lu",p.retainCount);
 9         
10         [q release];
11         NSLog(@"%lu",p.retainCount);
12         [p run];

创建一个Person类  一个p指针指向创建的对象    创建完成之后 计数器为1   创建一个q指针指向p原本指的对象 计数器+1为2 

[p release] 计数器-1   [q release] 计数器再-1 此时对象释放    

1 - (void)dealloc {
2     NSLog(@"人被释放了");
3     [super dealloc];
4 }

类中重写dealloc方法 则会输出 人被释放了  若开启僵尸对象检测 则[p run]报错

 

 

// 为什么最后不为0?

最后一次输出,引用计数没有变成0.

这是为什么呢?

因为该对象的内存已经被回收,而我们向一个已经被回收的对象发了一个retainCount消息,所以它的输出结果应该是不确定的,如果该对象所占的内存被复用了,那么就有可能造成程序异常崩溃。

 

那为什么在这个对象被回收之后,这个不确定的值是1而不是0呢?这是因为当最后一次执行release时,系统知道马上就要回收内存了,就没有必要再将retainCount减1了,因为不管减不减1,该对象都肯定会被回收,而对象被回收后,它的所有的内存区域,包括retainCount值也变得没有意义。不将这个值从1变成0,可以减少一次内存操作,加速对象的回收。

 

2.单个对象的内存管理

    2.1.1 内存泄露的第一种情况

1     Person * p = [[Person alloc] init];
2     NSLog(@"%lu",p.retainCount);
3     Person * q = [p retain];
4     NSLog(@"%lu",q.retainCount);
5     [p release];//引用两次需要两次release  少写一次都会内存泄露
6     [q release];

 2.1.2内存泄露的第二种情况

1     Person * p2 = [[Person alloc] init];
2     NSLog(@"%lu",p2.retainCount);
3     p2 = nil;
4     [p2 release]; // [nil release]

释放前指向nil   然后release 之前的对象没有被释放

 

  2.2.1 野指针的提前释放 

1         Person * p = [[Person alloc] init];
2         NSLog(@"%lu",p.retainCount);
3         Person * q = [p retain];
4         
5         [p release];
6        // p = nil;
7         [q release];
8         //q = nil;
9         [p run]; // [nil run]  // nil 调用任何方法都不会报错

提前释放之后再用对象的方法 会形成提前释放的野指针

 

  2.2.2野指针的重复释放

1         Person * p = [[Person alloc] init];
2         NSLog(@"%lu",p.retainCount);        
3         [p release];
4         [p release];
5 //重复释放 

释放之后 不可retain  无法起死回生 

 

3.多个对象的内存管理

 1         // 创建Person对象
 2         Person * p = [[Person alloc] init];
 3         // 创建Car对象
 4         Car * c = [[Car alloc] init];
 5         // 让人有一辆车
 6         [p setCar:c];
 7         [p drive];
 8         [c release];
 9         [p drive];
10         // 只要p对象存在,就可以随意调用自己的方法
11         [p release];
1 //Car类成员方法的实现以及dealloc的重写
2 - (void)dealloc {
3     NSLog(@"dealloc 车被销毁了");
4     [super dealloc];
5 }
6 
7 - (void)run {
8     NSLog(@"车跑起来了");
9 }
 1 //Person类成员方法的实现以及dealloc方法的重写
 2 
 3 - (void)setCar:(Car *)car {
 4     //在setCar的时候,让car的retainCount+1
 5     // 目的是保证p对象存在的时候,_car对象一定存在
 6     _car = [car retain];
 7 }
 8 
 9 - (Car *)car {
10     return _car;
11 }
12 
13 - (void)drive {
14     [_car run];
15 }
16 
17 - (void)dealloc {
18     NSLog(@"dealloc 人被销毁了");
19     // 保证p对象销毁的时候,他所持有的_car对象也被销毁,防止出现内存泄露
20     [_car release];
21     [super dealloc];
22 }

   set方法的内存管理  

若两次set方法的参数都为同一对象  那么会先把对象release一次之后再retain   但release之后就释放掉了  所以要判断参数是否和对象的成员相等

 1 - (void)setCar:(Car *)car {
 2     //在setCar的时候,让car的retainCount+1
 3     // 目的是保证p对象存在的时候,_car对象一定存在
 4     
 5     // 如果调用 _car = car,并且_car的retainCount=1 的情况下会出现野指针问题
 6     if (_car != car) {
 7         // 第一次运行 [nil release]
 8         [_car release];
 9         _car = [car retain];
10     }
11     
12 }

set方法可以改写为如下所示。

 

4.@property的参数

// 1与内存管理相关的参数

// 默认是 assign

// retain 生成符合内存管理原则的set方法

// assign 直接赋值,不考虑内存管理(一般基本数据类型)

// 2 与多线程相关的代码

// notatomic 不生成与多线程相关的代码 默认 iOS开发中用这个

// atomic 生成与多线程相关的代码 mac开发中会用到

// 3 是否生成set与get方法

// readonly 只生成get方法

// readwrite 生成get与set方法 默认

// 没有只生成set方法的参数

// 4 set与get方法名称相关的参数

// setter 设置set方法的名字(有冒号);

// getter 设置get方法的名字

 

5.循环引用问题

Person类有Car类的属性

Car类也有Person类的属性 就成为循环引用  

 

.h文件中导入头文件应换成 @class 类名 的形式 

前向声明 告诉系统有这样一个类

.m文件中应导入头文件 调用类的成员方法

引用关系应该一强一弱  

 1 #import <Foundation/Foundation.h>
 2 #import "Person.h"
 3 #import "Car.h"
 4 int main(int argc, const char * argv[]) {
 5     @autoreleasepool {
 6         // p 1 car 1
 7         Person * p = [[Person alloc] init];
 8         Car * car = [[Car alloc] init];
 9         // p 1 car 2
10         [p setCar:car];
11         // car 2  p 1
12         [car setPerson:p];
13         // p 0 car 1
14         [p release];      //难点总结:p release之后 计数count 变为0,此时调用 p 的 dealloc 函数,随之成员变量_car release
15         // p 0  car 0
16         
17         [car release];
18         
19         
20         
21         
22 //        // p 1 car 1
23 //        Person * p = [[Person alloc] init];
24 //        Car * car = [[Car alloc] init];
25 //        // p 1 car 1
26 //        [p setCar:car];
27 //        // car 1 p 2
28 //        [car setPerson:p];
29 //        // p 1 car 1
30 //        [p release];
31 //        // p 0  car 0
32 //        [car release];
33         
34         
35         
36         
37 //        // p 1 car 1
38 //        Person * p = [[Person alloc] init];
39 //        Car * car = [[Car alloc] init];
40 //        // p 1 car 2
41 //        [p setCar:car];
42 //        // car 2 p 2
43 //        [car setPerson:p];
44 //        // p 1 car 2
45 //        [p release];
46 //        // p 1 car 1
47 //        [car release];        
48        
49     }
50     return 0;
51 }
main.m
1 #import <Foundation/Foundation.h>
2 @class Car; // 前向声明 告诉系统有这样一个类
3 @interface Person : NSObject
4 @property (nonatomic,retain) Car * car;
5 @end
Person.h
1 #import "Person.h"
2 #import "Car.h"
3 @implementation Person
4 - (void)dealloc {
5     NSLog(@"person 被销毁了");
6     [_car release];
7     [super dealloc];
8 }
9 @end
Person.m
1 #import <Foundation/Foundation.h>
2 @class Person;
3 @interface Car : NSObject
4 @property (nonatomic,assign) Person * person;
5 @end
Car.h
1 #import "Car.h"
2 #import "Person.h"
3 @implementation Car
4 - (void)dealloc {
5     NSLog(@"car 被销毁了");
6 //    [_person release]; 弱引用 所以没有这行
7     [super dealloc];
8 }
9 @end
Car.m

 

 

6.autorelease的基本原理  

autorelease   作用为延迟释放   

1     @autoreleasepool {
2         Person * p = [[Person alloc] init];
3         //        [p release];// 立即减1
4         [p autorelease]; // 延迟减1 把对象添加到最近的自动释放池
5         NSLog(@"aaa");
6     }// 自动调用 [pool drain]
7    

也可以new一个NSAutoreleasePool对象      释放时调用[pool drain]方法  可以起到}的作用

 1     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 2     // {
 3     Person * p = [[Person alloc] init];
 4     [p autorelease];
 5     //        [p retain];
 6     Person * q = [[Person alloc] init];
 7     [q autorelease];
 8     NSLog(@"aaa");
 9     // 给池子里每一个对象发送release方法
10     [pool drain]; // [p release]
11     // }
12     NSLog(@"bbb");

 


1     Person * p = [[Person alloc] init];
2     // 自动释放池是可以随意创建的
3     // 不管这个对象是在@autoreleasepool之内还是之外创建的,只要你在池子中调用了autorelease方法,那么这个对象就会被放入到池子中,随着池子的销毁而销毁
4     // 创建一个自动释放池
5     @autoreleasepool {
6         [p autorelease];
7         NSLog(@"aaa");
8     }
9     NSLog(@"bbb");

    Person * p = [[Person alloc] init];
    @autoreleasepool {
        [p autorelease];
    
        @autoreleasepool {
            [p autorelease];
            NSLog(@"aaa");
        }//此时已经释放
        NSLog(@"bbb");
    }

 

 

7. autorelease的应用 

 1     @autoreleasepool {
 2 //        Chief * c = [[Chief alloc] init];
 3 //        [c autorelease];
 4         Chief * chief = [Chief chief];
 5         
 6         Food * food = [chief foodByChief];
 7         NSLog(@"%@",food.name);
 8         
 9         NSLog(@"aaa");
10     }
11     NSLog(@"bbb");
12     return 0;
13 
14 
15 
16 
17 
18 + (id)chief {
19     id chief = [[self alloc] init];
20     
21     return [chief autorelease];
22 }
23 
24 - (Food *)foodByChief {
25     Food * food = [[Food alloc] init];
26     food.name = @"辣条";
27     return [food autorelease];
28 }

 

 

8. ARC的使用 

 1 #import <Foundation/Foundation.h>
 2 #import "Dog.h"
 3 // __strong __weak
 4 
 5 void testStrong()
 6 {
 7     //只要有强指针指向一个对象,那么系统就不会回收该对象,只要没有强指针指向对象,系统立即回收该对象
 8     // 默认情况下所有的指针都是强指针类型
 9     // 在ARC中,不能调用retain release autorelease
10     // 也不能显式调用[super dealloc]
11     Dog * dog = [[Dog alloc] init];
12     // 对象一
13     NSLog(@"aaa");
14     //
15     Dog __weak * dog1 = [[Dog alloc] init]; // assign
16     
17     //        dog = nil;
18     Dog * dog2 = [[Dog alloc] init];
19     // 对象二
20     dog = dog2;
21     NSLog(@"%@ %@",dog,dog2);
22 }
23 
24 void testWeak()
25 {
26     Dog * dog = [[Dog alloc] init];
27     
28     Dog * __weak dog1 = dog;
29     NSLog(@"%@ %@",dog,dog1);
30     dog = nil;
31     NSLog(@"%@ %@",dog,dog1);
32     // 如果对于一个对象而言,这个对象既有强引用的指针,又有弱引用的指针,当强引用的指针不存在时,所有弱引用的指针会自动置空
33 }
34 
35 void testAutoreleaseing()
36 {
37     //
38     Dog * dog = nil;
39     Dog __autoreleasing * dog1 = nil
40     ;
41     
42     @autoreleasepool {
43         dog = [[Dog alloc] init];
44         NSLog(@"dog---%@",dog);
45         
46         dog1 = [[Dog alloc] init];
47         NSLog(@"dog1--%@",dog1);
48         
49     }
50     NSLog(@"dog------%@",dog);
51     NSLog(@"dog1------%@",dog1);
52 }
53 
54 void testUnsafe_unretained()
55 {
56     // __unsafe_unretained只要指向对象的指针至少有一个强引用存在,那么 __unsafe_unretained和__weak功能一样
57     // 若没有强引用的指针后,__weak类型的指针会自动置空,而__unsafe_unretained类型的指针不会
58     Dog __unsafe_unretained * dog = nil;
59     {
60         Dog * dog11 = [[Dog alloc] init];
61         dog = dog11;
62         dog11 = nil;
63         NSLog(@"%@",dog);
64     }
65 }
66 
67 
68 int main(int argc, const char * argv[]) {
69     @autoreleasepool {
70         testUnsafe_unretained();
71         
72     }
73     return 0;
74 }
 1 #import "Dog.h"
 2 
 3 @implementation Dog
 4 - (void)dealloc {
 5     NSLog(@"dog 被释放了");
 6     // 不能显式调用[super dealloc]
 7     // 不能调用release autorelease retain
 8     // 不能查看retainCount
 9 //    [super dealloc];
10 }
11 @end

 

 

 

9.MRC与ARC的混编

 

Dog类为ARC     在MRC中运行时需要更改的设置

 

 

Car和Engine为MRC   在ARC中运行时需要更改的设置

 

posted @ 2015-11-06 01:26  GXcoder  阅读(234)  评论(0编辑  收藏  举报