Objective-C 内存管理 | 青训营笔记

106 阅读5分钟

这是我参与「第四届青训营」笔记创作活动的第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 栈和堆

image.png

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

image.png

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
    • 可以复制赋值操作
    • 对象+引用计数
    • image.png

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内存管理详解