iOS内存管理相关

255 阅读8分钟

MRC 相关

对象的 Setter 方法

- (void)setName:(NSString *)name {
    if (_name != name) { //1
        [_name release]//2
        _name = [name retain]//3
    }
}
  • 在 MRC 模式下,setter 方法的处理方式为了:
  1. 防止重复_name 可能会被提前释放,导致调用_name 是僵尸对象而崩溃;
  2. 将旧对象_name 提前释放,防止内存泄露;
  3. 将对象 name 的 retain 引用计数加一,防止对象被释放而导致使用报错问题;

对象深拷贝与浅拷贝

  • 不可变对象的 copy 方法执行的是浅拷贝操作,可变对象的 copy 方法执行的是深拷贝操作。
  • 不可变对象的 mutableCopy 方法执行的是深拷贝操作,可变对象的 mutableCopy 方法执行的是深拷贝操作。

对象使用 copy 或 strong 修饰符

@property (nonatomiccopyNSMutableArray *array;
@property (nonatomicstrongNSMutableArray *array;
  • 这两行代码的本质区别就是在 setter 方法里面执行的操作不同:
    • copy 修饰符的属性,在 setter 方法中会执行[array copy]方法,会导致可变数组变成了不可变数组了;
    • strong 修饰符的属性,在 setter 方法中会执行[array retain]方法,只是将引用计数加一,保护当前对象不被释放;
    • 一般字符串操作就是使用 copy,而对于可变的对象,都是使用 strong 来进行修饰!!

autoreleasepool

自动释放池

int main(int argc, const char * argv[]) {
    @autoreleasepool {

    }
    return 0;
}

将 autoreleasepool 转换为底层 C++源码,可以知道底层是一个结构体__AtAutoreleasePool 构成的!

struct __AtAutoreleasePool {
  __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
  ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
  void * atautoreleasepoolobj;
};

AutoreleasePoolPage 结构

class AutoreleasePoolPage
{
    static size_t const SIZE = PAGE_MAX_SIZE;  //4096
    static size_t const COUNT = SIZE / sizeof(id);
    magic_t const magic; //检验AutoreleasePoolPage完整性
    id *next; //指向下一个存放autorelease对象的地址
    pthread_t const thread; //当前页所在线程
    AutoreleasePoolPage * const parent; //当前page的parent指针
    AutoreleasePoolPage *child; //当前page的child指针
    uint32_t const depth;
    uint32_t hiwat;
}
  • AutoreleasePoolPage 可使用的最大内存空间为 4096 个字节,其中的变量所占用的内存大小为 56 个字节!
  • AutoreleasePoolPage 其实是一个双向链表的数据结构存储方式,将每个 page 通过 parent 和 child 进行互相链接,可以通过链表查找的方式找到想要释放的对象!

原理

  • 假设地址从 0x1000 开始到 0x2000,总共占用的内存空间为 4096 个字节,其中变量到 0x1038 的字节大小为 56 个字节。
  • 如果 AutoreleasePoolPage 中变量所占的字节大于 4096,那将会重新生成一个新的 page 继续存储对象。

runtime 源码

static inline void *push()
{
     id *dest;
     if (DebugPoolAllocation) {
         //创建新的page
         dest = autoreleaseNewPage(POOL_BOUNDARY);
      } else {
        //获取page,没有或者page满了就创建新的
         dest = autoreleaseFast(POOL_BOUNDARY);
      }
      return dest;
}
static inline id *autoreleaseFast(id obj)
{
     AutoreleasePoolPage *page = hotPage();
     if (page && !page->full()) { //hot的page不满直接将obj入栈
         return page->add(obj);
     } else if (page) { //创建新的page将obj对象入栈
         return autoreleaseFullPage(obj, page);
     } else { //没有page则创建新的page
         return autoreleaseNoPage(obj);
     }
}

//page已经满了,获取未存满的page或创建新的page
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
      do {
          if (page->child) page = page->child; //遍历获取获取最后一个未存满的page
          //创建新的page
          else page = new AutoreleasePoolPage(page);
      } while (page->full());
      setHotPage(page);
      return page->add(obj);
}

//创建新的page并将obj入栈
id *autoreleaseNoPage(id obj)
{
      // 初始化page并将POOL_BOUNDARY传入做为第一个栈底对象
      AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
      setHotPage(page);
      if (pushExtraBoundary) {
          page->add(POOL_BOUNDARY);
      }
      // 将obj对象入栈
      return page->add(obj);
}

//获取新的page
id *autoreleaseNewPage(id obj)
{
     AutoreleasePoolPage *page = hotPage(); //获取正在使用的page
     if (page) return autoreleaseFullPage(obj, page); //获取新的page
     else return autoreleaseNoPage(obj); // 初始化page
}
  • 如果当前没有 page,则创建新的 page,并将 POOL_BOUNDARY 做为第一个对象压入栈顶,再将 obj 对象添加到 page 中;
  • 如果当前存在 page 并且 page 未存满,则将当前 obj 对存入栈中;若当前 page 已存满,则创建新的 page 并将 POOL_BOUNDARY 做为第一个对象压入栈顶,再将 obj 对象添加到 page 中;
static inline void pop(void *token)
{
      AutoreleasePoolPage *page;
      id *stop;
      page = pageForPointer(token); //根据对象token查找token是在哪一个page中
      stop = (id *)token;
      page->releaseUntil(stop); //从hotpage释放对象直到stop
      //根据不同情况将page进行释放,并将page置nil
      if (DebugPoolAllocation  &&  page->empty()) {
          AutoreleasePoolPage *parent = page->parent;
          page->kill();
          setHotPage(parent);
      } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
          page->kill();
          setHotPage(nil);
      }
      else if (page->child) {
          if (page->lessThanHalfFull()) {
              page->child->kill();
          }
          else if (page->child->child) {
              page->child->child->kill();
          }
      }
}
  • 调用 pop 传入 token 对象会对 page 进行变量查找在哪一个 page 中,并将对象进行释放知道 token 所在位置,并将多余的 page 进行释放;

对象 dealloc

runtime 源码

- (void)dealloc {
    _objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
    obj->rootDealloc();
}
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // 如果是TaggedPointer直接返回

    if (fastpath(isa.nonpointer //没使用位域来存放信息  &&
                 !isa.weakly_referenced //没被弱引用  &&
                 !isa.has_assoc //没关联对象  &&
                 !isa.has_cxx_dtor //没析构函数  &&
                 !isa.has_sidetable_rc //引用计数没存储在sidetable表中))
    {
        assert(!sidetable_present());
        free(this); //直接释放对象
    }
    else {
        object_dispose((id)this);
    }
}
object_dispose(id obj)
{
    objc_destructInstance(obj);
    free(obj);
}
void *objc_destructInstance(id obj)
{
    if (obj) {
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }

    return obj;
}
  • object_cxxDestruct:

    • 执行了正要释放的 obj 对象相关对象的释放;
    • 执行了父类相关对象的释放;
  • _object_remove_assocations:

    • 从 AssociationsHashMap 哈希表中移除当前对象相关联的 assocation 对象;
  • clearDeallocating:

    • 若对象被其它对象弱引用着,则将弱引用对象置为 nil,并将当前对象从 weak_table 弱引用表中删除;
    • 若当前对象有将引用计数存放在 sidetable 表中,则将对象从 sidetable 表中移除;

参考资料: 探索 dealloc 真谛

扫码_搜索联合传播样式-标准色版.png