这是我参与「第四届青训营」笔记创作活动的第4天
一、本堂课重点内容:
本节课主要学习设计iOS内存管理的各种方法。
二、详细知识点介绍:
课前准备
1.一个可运行的Xcode工程 2.几个Class
// Person.h
@protocol PersonDelegate <NSObject>
- (void)walkDog;
@end
@interface Person : NSObject
@property (nonatomic, strong) Dog *dog;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, weak) id<PersonDelegate> delegate;
@property (nonatomic, assign) int age;
- (instancetype) initWithName:(NSString *)name;
@end
// Friend.h
@interface Friend : Person <PersonDelegate>
@property (nonatomic, strong) Person *myFriend;
- (void)walkDog;
@end
// Dog.h
@interface Dog : NSObject
- (void)bark;
@end
1. Introduction 介绍
Stack & Heap 栈和堆
c内存管理
用 malloc & free 动态分配内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void stackFunc() {
// they are all in stack
int b = 10;
char s[] = "bb";
printf("the b is %d\n", b);
}
void heapFunc() {
// j is in heap
int* g = malloc(sizeof(int));
printf("the g is %d\n", *g);
free(g);
}
int main() {
stackFunc();
heapFunc();
}
c++内存管理
new & delete
void useRawPointer()
{
// Using a raw pointer -- not recommended.
std::string *pSong = new std::string("Nothing on You");
int *p = new int[10];
std::cout << *pSong << std::endl;
// Don't forget to delete!
delete pSong;
delete[] p;
}
智能指针
- 保留 -> 和 * 运算符
- auto_pt & unique_ptr
- 基于排他所有权模式:两个指针不能指向同一个资源
- 无法进行左值unique_ptr复制构造,也无法进行左值复制赋值操作,但允许临时右值赋值构造和赋值
- shared_ptr
- 可以复制赋值操作
- 对象+引用计数
Reference Counting 引用计数
- 记录了当前内存资源到底有多少指针在引用(可以访问这个资源)
- 当新增加一个可以访问这个资源的引用,计数器会加1(e.g. Copy),反之会减去1
- 当引用计数 = 0时,对象会被销毁
2.iOS内存管理
iOS 内存管理的核心是管理「(强)引用计数 (Reference Counting)」
在当前代码中,我们基本只用考虑 ARC(自动管理引用计数)
Objective C内存管理
- 我们需要管理什么?
- 继承 NSObject 对象会分配在堆里面
- 任何继承 NSObject 对象都需要进行内存管理
- 为什么要管理内存?
- 多个对象之间相互强引用,导致不能释放,无法让系统回收
- 如果一个程序占用内存过多,系统可能会强制关闭程序,造成crash
- 如果提前释放指针,会导致野指针,也会造成crash
- 术语
- 持有 = retain = 引用计数 +1
- 释放 = release = 引用计数 -1
MRC(Manual Reference Counting/手动管理引用计数)
- 在OC中,使用引用计数来进行内存管理。每个对象都有一个与其相对应的引用计数器,当持有一个对象,这个对象的引用计数就会递增;当这个对象的某个持有被释放,这个对象的引用计数就会递减。当这个对象的引用计数变为0,那么这个对象就会被系统回收。
- “alloc”, “new”, “copy”, or “mutableCopy” 开头的方法创建的对象,引用计数 = 1
// 使用了alloc分配了内存,obj指向了对象,该对象本身引用计数为1,不需要retain
id obj = [[NSObject alloc] init];
// 使用了new分配了内存,objc指向了对象,该对象本身引用计数为1,不需要retain
id obj = [NSObject new];
- 对一个对象发送 retain 消息,可以让对象的引用计数 +1,
- 对一个对象发送 release 消息,可以让对象的引用计数 -1。
- 当对象的引用计数为 0 时,即将销毁的时候,系统会向对象发送一条dealloc消息 【不能直接调用 dealloc】。
//NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的
// 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的)
id obj1 = [NSMutableArray array];
[obj1 retain];
//NSMutableArray通过类方法array产生了对象(并没有使用alloc、new、copy、mutableCopt来产生对象),因此该对象不属于obj自身产生的
// 因此,需要使用retain方法让对象计数器+1,从而obj可以持有该对象(尽管该对象不是他产生的)
id obj1 = [NSMutableArray array];
[obj1 retain];
// 释放一个不属于自己的对象
id obj = [NSMutableArray array];
//obj没有进行retain操作而进行release操作,然后autoreleasePool也会对其进行一次release操作,导致奔溃。
[obj release];
Autorelease-Pool
- 对象会放到自动释放池,统一释放
- autorelease和release的区别是:
- release是马上释放对某个对象的强引用;
- autorelease是延迟释放某个对象。
- 在部分场景下,使用Autorelease pool可以降低内存峰值
ARC (Automatic Reference Counting/自动管理引用计数)[重点]
- retain/release都不用考虑
- 只需要初始化的时候
[[NSObject alloc] init] - 可以自定义
dealloc
- 只需要初始化的时候
- 只要有一个强指针在内存指向对象,对象就不能释放
- ARC 销毁时机是强引用的个数 = 0
- 而不是引用计数 = 0
- 默认所有对象变量的指针都是强指针
- 弱指针需显式声明
-
__weak Person *p2 = [Person new];
三、实践练习例子:
例子均在知识点介绍中,无实战项目。
四、课后个人总结:
-
本章有什么知识点不容易掌握?
内存管方法较多,难以记忆,多查多看多用。 -
什么地方容易与其他内容混淆?
深浅拷贝/强弱指针等概念易混淆。
五、引用参考:
学习手册:juejin.cn/post/712344…
课程PPT:Objective-C 内存管理.pptx
掘金文章:iOS内存管理详解