C/C++/Objective-C/Swift 内存管理总结

3,051 阅读3分钟

c内存管理

Malloc

void* malloc(size_t size)

  • 在堆中分配一个长度为size字节的连续空间,返回一个指向所分配的连续存储域的起始地址的指针void* (可以转换为任何其它类型的指针)。
  • 分配的内存未初始化,它们的值是未知的。
  • 当函数未能成功分配存储空间时(如内存不足)则返回一个NULL指针。
uint16_t *p = (uin16_t *)malloc(sizeof(uint16_t));
if (NULL == p)
{
    printf("malloc error\r\n");
} else
{
    memset(p, 0, n * sizeof(uint16_t)); // 内存的值未知, 需要初始化为0
}

calloc

void *calloc(int num, int size)

  • 分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0

realloc

void *realloc(void *address, int newsize);

  • 重新分配内存,把内存扩展到 newsize。
  • 从堆上当前内存段后面的字节中获得更多的内存空间,如果能够满足,则返回原指针;
  • 如果后面的空闲字节不够,那么就使用堆上第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,而将原来的数据块释放掉。
  • 如果内存不足,重新申请空间失败,则返回NULL。
  • 此时原来指针仍然有效,

memcpy

memcpy(void *dst, void *src, size_t n)

  • 从源内存区域复制数据到目标内存区域。

memmove

memcpy(void *dst, void *src, size_t n)

功能和 memcpy 相似, 但它可以处理dst和src重叠的内存区域

free

void free(void *address);

  • 调用free释放内存后,不能再访问被释放的内存空间, 此时的指针变成悬空指针, 所以最好手动赋值NULL
  • 不能两次释放相同的指针。因为释放内存空间后,该空间就交给了内存分配子程序,再次释放内存空间会导致错误。
  • 必须提供内存的起始地址,不能够提供部分地址,释放内存中的一部分是不允许的。
  • malloc和free成对使用, 避免内存泄漏

C++内存管理

new

  1. 调用malloc申请并分配内存。
  2. 调用构造函数, 初始化相应类型的对象,并返回首地址。
  • new运算符会抛出std::bad_alloc异常,如果加入nothrow,则不抛出异常,而是返回nullptr。

delete

  1. 调用析构函数
  2. 释放内存(free)

new[]/delete[]

初始化或者释放对象数组

按照对象的个数,分别调用构造函数和析构函数

unique_ptr

  • 拥有对象独有所有权语义的智能指针
  • unique_ptr 不共享它的指针。它无法复制到其他 unique_ptr,只能移动unique_ptr。
  • 移动后, 内存资源所有权将转移到另一unique_ptr,并且原始 unique_ptr 不再拥有此资源。
    unique_ptr<int> pInt(new int(8));
    unique_ptr<int> pInt2 = std::move(pInt);    // 转移所有权
    //cout << *pInt << endl; // 出错,pInt为空
    cout << *pInt2 << endl;

shared_ptr

  • 拥有共享对象所有权语义的智能指针
  • 记录对象被引用的次数,当引用次数为 0 的时候,也就是最后一个指向该对象的共享指针析构的时候,共享指针的析构函数就把指向的内存区域释放掉。
  • 它所指向的资源具有共享性,即多个shared_ptr可以指向同一份资源,并在内部使用引用计数机制来实现这一点。

weak_ptr

管理对象的弱引用

OC内存管理

引用计数

  • alloc/retain/copy 引用计数+1
  • release 引用计数 -1
  • autorelease 出了autoreleasepool(自动释放池))后, 引用计数再 -1
  • 凡是使用了alloc、retain或者copy让内存的引⽤用计数增加了,就需要使用release或者autorelease让内存的引用计数减少。需要一一对应
  • 当引用计数降为0之后,不应该再使用这块内存空间;

dealloc

当引用计数减到0的时候,系统自发调用dealloc方法释放内存。

ARC

  • 自动引用计数,编译器帮我们管理对象的retainCount值
  • 当开启ARC后, 上述手动改变引用计数的方法就无法使用了
  • ARC是编译器特性,编译器在编译的时候,会在合适的位置,自动插入内存管理的代码

Swift内存管理

Swift无法使用MRC,只支持ARC

strong

默认定义的对象全是强引用,引用计数+1

weak

  • 定义弱引用, 不会改变对象的引用计数
  • 弱引用变量必须是可选类型的var,因为对象销毁后,ARC会自动将弱引用设置为nil
weak var p: Person? = Person()

unowned

  • 定义无主引用, 不会改变对象的引用计数
  • 对象销毁后仍然存储着实例的内存地址,不会自动设置nil
  • 对象销毁后访问无主引用,会产生运行时错误(野指针)。