这是我参与「第四届青训营」笔记创作活动的的第3天
1 Stack , Heap
1.1 栈 Stack
遵循FILO原则
是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
1.2 堆 Heap
- 与Heap数据结构无关
- 手动分配
- 向高地址扩展
- 存储数组和对象, 线程共享
2 C和C++内存管理
C:用 malloc & free 动态分配内存 C++:
2.1 new & delete
会调用构造函数和析构函数进行初始化与清理,可自动计算类型的大小,管理内存失败会抛出异常。
2.2 Smart Pointer 智能指针
- 保留 -> 和 * 运算符
-
auto_pt & unique_ptr
- 基于排他所有权模式:两个指针不能指向同一个资源
- 无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值 shared_ptr
-
可以复制赋值操作
- 对象+引用计数
2.3 Reference Counting 引用计数
- 记录了当前内存资源到底有多少指针在引用(可以访问这个资源)
- 当新增加一个可以访问这个资源的引用,计数器会加1(e.g. Copy),反之会减去1
- 当引用计数= 0时, 对象会被销毁
3 iOS内存管理
核心:管理(强)引用计数 (Reference Counting),只用考虑 ARC
-
我们需要管理什么?
- 继承 NSObject 对象会分配在堆里面
- 任何继承 NSObject 对象都需要进行内存管理
-
为什么要管理内存?
- 多个对象之间相互强引用,导致不能释放,无法让系统回收
- 如果一个程序占用内存过多,系统可能会强制关闭程序,造成crash
- 如果提前释放指针,会导致野指针,也会造成crash
-
持有 = retain = 引用计数 +1
-
释放 = release = 引用计数 -1
3.1 Autorelease-Pool 自动释放池
对象会放到自动释放池,统一释放
autorelease:延迟释放某个对象
3.2 ARC
-
retain/release都不用考虑
- 只需要初始化的时候 [NSObject alloc] init]
- 可以自定义 dealloc
-
只要有一个强指针在内存指向对象,对象就不能释放
- ARC 销毁时机是强引用的个数 = 0
- 而不是引用计数 = 0
-
默认所有对象变量的指针都是强指针
- 弱指针需显式声明
__weak Person *p2 = [Person new];
4 Practical Memory Management
4.1 @property 修饰符
- strong: 拥有属性对象,强持有,只有OC对象才能使用该属性
- weak: 不拥有属性对象,弱持有,只有OC对象才能使用该属性
- assign: 指定 setter 使用简单赋值,一般用于基本类型/NSInteger/CGRect
@property (nonatomic, strong) People *people;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, weak) id<PersonDelegate> delegate;
@property (nonatomic, assign) int age;
4.2 循环引用
Delegate 与弱引用
@property (nonatomic, weak) id<PersonDelegate> delegate;
正常代码:
Person *person1 = [[Person alloc] init];
Dog * dog1 = [Dog new];
Friend *f1 = [Friend new];
person1.dog = dog1;
person1.delegate = f1;
f1.myFriend = person1;
循环引用代码:(永远有强指针指向,无法释放)
@property (nonatomic, strong) id<PersonDelegate> delegate;
5 浅拷贝,深拷贝
- 浅拷贝:直接将一个引用数据类型的地址,赋值给另一个变量存储的,两个变量存储的是相同的内存地址,一个操作,另一个也会改变
- 深拷贝:只复制引用数据类型中的数值而不是复制地址
不可变对象
- copy 【深拷贝】
- mutableCopy【深拷贝】
可变对象
- copy 【深拷贝】
- mutableCopy 【深拷贝】
5.1 可变与不可变
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) NSString *strongName;
@end
NSString
NSString *stringA = @"stringA";
NSLog(@"本身string的地址:%p",stringA);
Person *person1 = [[Person alloc] init];
person1.name = stringA;
NSLog(@"copy string的地址:%p",person1.name);
person1.strongName = stringA;
NSLog(@"strong string的地址:%p",person1.strongName);
-
copy:浅拷贝
-
strong : 持有stringA
NSMutableString
可变对象可以直接赋值给不可变对象属性
NSMutableString *stringM = [[NSMutableString alloc] initWithString:@"stringM"];
NSLog(@"本身mutablString的地址:%@",stringM);
Person *person1 = [[Person alloc] init];
person1.name = stringM;
NSLog(@"copy string的地址:%p",person1.name);
person1.strongName = stringM;
NSLog(@"strong string的地址:%p",person1.strongName);
-
copy:深拷贝
-
strong : 持有stringA
6 野指针与Crash
当所指向的对象被提前释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址, 若操作系统将这部分已经释放的内存重新分配给另外一个进程, 而原来的程序重新引用现在的野指针,则将产生无法预料的后果。 因为此时野指针所指向的内存现在包含的已经完全是不同的数据。
7 小结
这节课讲了比较重要的Objective-C 内存管理,其中循环引用与深拷贝浅拷贝,可变与不可变较为重要,在开发过程中需要注意。