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 }

1 #import <Foundation/Foundation.h>
2 @class Car; // 前向声明 告诉系统有这样一个类
3 @interface Person : NSObject
4 @property (nonatomic,retain) Car * car;
5 @end

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

1 #import <Foundation/Foundation.h>
2 @class Person;
3 @interface Car : NSObject
4 @property (nonatomic,assign) Person * person;
5 @end

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
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中运行时需要更改的设置