Object-C内存管理解析一

一、关于引用计数:

读了《Objective-C高级编程(iOS与OS X多线程和内存管理)》,发现吊炸天的感觉,所以记录下。嗯 还是自己太菜了,好像是7年前的书了。 自己尽然没有去认真的去研读,惭愧,惭愧!

  • ARC之前引用计数是得程序员自己管理的,ARC之后是编译器帮我们管理的。但是 是不是就不用了解呢?如果你还在用OC的话绝对用处很大,农村人,不讲套路。
  • 引用计数,引用书中的例子:

最早进入办公室的人开灯 ->最后离开办公室的人关灯
中间进入就是++,离开--

  • 管理方式:

1.自己生成的对象,自己持有
2.非自己生成的对象,自己也能持有
3.不需要自己持有持有的对象时释放
4.非自己持有的对象无法释放
以上就是对引用计数的概括,有点枯燥,但是把内存管理读完再来看,就非常美妙。

二、alloc/retain/release/dealloc实现

  • 必须先搞定这几个鬼东西,这几个可是OC内存管理的基石,按照惯例,思维导图 怼一发:


    图片.png
  • 密密麻麻,看着是不是很反感。跟着走进去你会发现花姑娘
    因为NSObject不开源,所以这个GNUstep现在是2.7.0版本。这个鬼基本由于NSObject内部实现方法一样,查不了多少,还有一个runtime的源代码也准备好 开搞
1.alloc方法的实现

-在base/Source/Foundation/NSObject.m的+ (id) alloc方法里,最后调用

NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)
// 简化后主要代码

// size:计算容纳对象所需内存的大小 
//obj_layout:保存着引用计数retained 
// 苹果用哈希表实现 GNUstep用占用头部内存实现
 int size = class_getInstanceSize(aClass) + extraBytes + sizeof(struct obj_layout);
  if (zone == 0)
    {
      // NSZone是为防止内存碎片化引入的结构,对内存分配区域进行多重化管理
      // 根据对象的目的、大小、分配
      zone = NSDefaultMallocZone();
    }
    // 给对象分配内存空间
  new = NSZoneMalloc(zone, size);
  if (new != nil)
    {
     // 将内存空间置为0
      memset (new, 0, size);
      new = (id)&((obj)new)[1];
      object_setClass(new, aClass);
      AADD(aClass, new);
    }
  // 返回对象指针
  return new;
2.retain方法的实现
  • 在base/Source/Foundation/NSObject.m的- (id) retain方法里,最后调用
static id retain_fast(id anObject)

1.weak修饰的时候返回本身不做处理
2.static id objc_retain_fast_np_internal(id anObject)主要的就是 ((obj)anObject)[-1].retained++;
3.这里说明一下 ((obj)anObject)[-1]GNUstep用占用头部内存实现,就是obj返回的指针-1就是指向retained的内存地址,苹果用哈希表实现会有点不同

3.release方法的实现

-在base/Source/Foundation/NSObject.m的- (oneway void) release方法里,最后调用

static void release_fast(id anObject)

1.weak修饰的时候返回本身不做处理
2.引用计数--((obj)anObject)[-1].retained--
3.引用计数为0时调用dealloc方法

4.dealloc方法的实现

-在base/Source/Foundation/NSObject.m的- (void) dealloc方法里

NSDeallocateObject(id anObject)
// 简化后代码

NSDeallocateObject(id anObject)
{
  // 获取类对象
  Class aClass = object_getClass(anObject);
   // 判断 当前类对象 元类对象不为空
  if ((anObject != nil) && !class_isMetaClass(aClass))
    {
     //ARC下
      obj   o = &((obj)anObject)[-1]; // 获取对象指针
      NSZone    *z = NSZoneFromPointer(o); // tagePoint寻址
      if (NSZombieEnabled == YES)
    {
        // 如果僵尸对象散列表不为空 放进僵尸对象散列表
      if (0 != zombieMap)
        {
              pthread_mutex_lock(&allocationLock);
          NSMapInsert(zombieMap, (void*)anObject, (void*)aClass);
              pthread_mutex_unlock(&allocationLock);
        }
        // 释放僵尸对象 或者 添加僵尸对象
      if (NSDeallocateZombies == YES)
        {
          object_dispose(anObject);
        }
      else
        {
          object_setClass(anObject, zombieClass);
        }
    }
      else
    {
      object_dispose(anObject);
    }
    }
  return;
}
  • 上面注释写的很清楚了,我主要把非ARC下的代码删了,retain和release主要的说清楚,其他的大家可以自己看看,然后发现object_dispose()方法没有了,不急因为object_dispose()是runtime的东西
  • 在runtime源码objc/Source/objc-runtime-new.mm里面的
id  object_dispose(id obj)
  • 释放之前还调用了objc_destructInstance(obj)方法
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        // 判断是否有C++析构函数
        bool cxx = obj->hasCxxDtor();
        // 是否有关联对象
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        // 清除有C++析构函数
        if (cxx) object_cxxDestruct(obj);
        // 清除关联对象表 AssociationsManager对应的value
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }
    return obj;
}
  • 最后调用了obj->clearDeallocating()可以自己进去看看,思维导图里面注释的比较清楚如图:


    图片.png
  • 至于从何得知void *objc_destructInstance(id obj) 的方法

// 判断是否有C++析构函数
bool cxx = obj->hasCxxDtor();
// 是否有关联对象
bool assoc = obj->hasAssociatedObjects();

  • 可以根据isa指针联合体里面有记录的,大致如下:
union isa_t 
struct {
    // 1代表优化后的使用位域存储更多的信息。
    uintptr_t nonpointer        : 1; 

   // 是否有设置过关联对象
    uintptr_t has_assoc         : 1;

    // 是否有C++析构函数
    uintptr_t has_cxx_dtor      : 1;

    // 存储着Class对象的内存地址信息
    uintptr_t shiftcls          : 33; 

    // 用于在调试时分辨对象是否未完成初始化
    uintptr_t magic             : 6;

    // 是否有被弱引用指向过。
    uintptr_t weakly_referenced : 1;

    // 对象是否正在释放
    uintptr_t deallocating      : 1;

    // 引用计数器是否过大无法存储在isa中
    // 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
    uintptr_t has_sidetable_rc  : 1;

    // 里面存储的值是引用计数器减1
    uintptr_t extra_rc          : 19;
};
  • 今天就到这了,小子头昏眼花,急需按一波。陆续会更新内存管理的内容,各位看官,回见
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容