Object-C內(nèi)存管理解析一

一橄务、關(guān)于引用計(jì)數(shù):

讀了《Objective-C高級(jí)編程(iOS與OS X多線(xiàn)程和內(nèi)存管理)》蜂挪,發(fā)現(xiàn)吊炸天的感覺(jué)棠涮,所以記錄下严肪。嗯 還是自己太菜了驳糯,好像是7年前的書(shū)了。 自己盡然沒(méi)有去認(rèn)真的去研讀隧枫,慚愧官脓,慚愧卑笨!

  • ARC之前引用計(jì)數(shù)是得程序員自己管理的赤兴,ARC之后是編譯器幫我們管理的桶良。但是 是不是就不用了解呢陨帆?如果你還在用OC的話(huà)絕對(duì)用處很大疲牵,農(nóng)村人纲爸,不講套路读虏。
  • 引用計(jì)數(shù),引用書(shū)中的例子:

最早進(jìn)入辦公室的人開(kāi)燈 ->最后離開(kāi)辦公室的人關(guān)燈
中間進(jìn)入就是++题翻,離開(kāi)--

  • 管理方式:

1.自己生成的對(duì)象嵌赠,自己持有
2.非自己生成的對(duì)象姜挺,自己也能持有
3.不需要自己持有持有的對(duì)象時(shí)釋放
4.非自己持有的對(duì)象無(wú)法釋放
以上就是對(duì)引用計(jì)數(shù)的概括,有點(diǎn)枯燥词渤,但是把內(nèi)存管理讀完再來(lái)看缺虐,就非常美妙高氮。

二剪芍、alloc/retain/release/dealloc實(shí)現(xiàn)

  • 必須先搞定這幾個(gè)鬼東西窖铡,這幾個(gè)可是OC內(nèi)存管理的基石费彼,按照慣例箍铲,思維導(dǎo)圖 懟一發(fā):


    圖片.png
  • 密密麻麻小染,看著是不是很反感裤翩。跟著走進(jìn)去你會(huì)發(fā)現(xiàn)花姑娘
    因?yàn)镹SObject不開(kāi)源踊赠,所以這個(gè)GNUstep現(xiàn)在是2.7.0版本今穿。這個(gè)鬼基本由于NSObject內(nèi)部實(shí)現(xiàn)方法一樣,查不了多少拔创,還有一個(gè)runtime的源代碼也準(zhǔn)備好 開(kāi)搞
1.alloc方法的實(shí)現(xiàn)

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

NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone)
// 簡(jiǎn)化后主要代碼

// size:計(jì)算容納對(duì)象所需內(nèi)存的大小 
//obj_layout:保存著引用計(jì)數(shù)retained 
// 蘋(píng)果用哈希表實(shí)現(xiàn) GNUstep用占用頭部?jī)?nèi)存實(shí)現(xiàn)
 int size = class_getInstanceSize(aClass) + extraBytes + sizeof(struct obj_layout);
  if (zone == 0)
    {
      // NSZone是為防止內(nèi)存碎片化引入的結(jié)構(gòu)侣滩,對(duì)內(nèi)存分配區(qū)域進(jìn)行多重化管理
      // 根據(jù)對(duì)象的目的君珠、大小、分配
      zone = NSDefaultMallocZone();
    }
    // 給對(duì)象分配內(nèi)存空間
  new = NSZoneMalloc(zone, size);
  if (new != nil)
    {
     // 將內(nèi)存空間置為0
      memset (new, 0, size);
      new = (id)&((obj)new)[1];
      object_setClass(new, aClass);
      AADD(aClass, new);
    }
  // 返回對(duì)象指針
  return new;
2.retain方法的實(shí)現(xiàn)
  • 在base/Source/Foundation/NSObject.m的- (id) retain方法里,最后調(diào)用
static id retain_fast(id anObject)

1.weak修飾的時(shí)候返回本身不做處理
2.static id objc_retain_fast_np_internal(id anObject)主要的就是 ((obj)anObject)[-1].retained++;
3.這里說(shuō)明一下 ((obj)anObject)[-1]GNUstep用占用頭部?jī)?nèi)存實(shí)現(xiàn),就是obj返回的指針-1就是指向retained的內(nèi)存地址,蘋(píng)果用哈希表實(shí)現(xiàn)會(huì)有點(diǎn)不同

3.release方法的實(shí)現(xiàn)

-在base/Source/Foundation/NSObject.m的- (oneway void) release方法里产上,最后調(diào)用

static void release_fast(id anObject)

1.weak修飾的時(shí)候返回本身不做處理
2.引用計(jì)數(shù)--((obj)anObject)[-1].retained--
3.引用計(jì)數(shù)為0時(shí)調(diào)用dealloc方法

4.dealloc方法的實(shí)現(xiàn)

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

NSDeallocateObject(id anObject)
// 簡(jiǎn)化后代碼

NSDeallocateObject(id anObject)
{
  // 獲取類(lèi)對(duì)象
  Class aClass = object_getClass(anObject);
   // 判斷 當(dāng)前類(lèi)對(duì)象 元類(lèi)對(duì)象不為空
  if ((anObject != nil) && !class_isMetaClass(aClass))
    {
     //ARC下
      obj   o = &((obj)anObject)[-1]; // 獲取對(duì)象指針
      NSZone    *z = NSZoneFromPointer(o); // tagePoint尋址
      if (NSZombieEnabled == YES)
    {
        // 如果僵尸對(duì)象散列表不為空 放進(jìn)僵尸對(duì)象散列表
      if (0 != zombieMap)
        {
              pthread_mutex_lock(&allocationLock);
          NSMapInsert(zombieMap, (void*)anObject, (void*)aClass);
              pthread_mutex_unlock(&allocationLock);
        }
        // 釋放僵尸對(duì)象 或者 添加僵尸對(duì)象
      if (NSDeallocateZombies == YES)
        {
          object_dispose(anObject);
        }
      else
        {
          object_setClass(anObject, zombieClass);
        }
    }
      else
    {
      object_dispose(anObject);
    }
    }
  return;
}
  • 上面注釋寫(xiě)的很清楚了姻僧,我主要把非ARC下的代碼刪了赌莺,retain和release主要的說(shuō)清楚挎扰,其他的大家可以自己看看遵倦,然后發(fā)現(xiàn)object_dispose()方法沒(méi)有了,不急因?yàn)閛bject_dispose()是runtime的東西
  • 在runtime源碼objc/Source/objc-runtime-new.mm里面的
id  object_dispose(id obj)
  • 釋放之前還調(diào)用了objc_destructInstance(obj)方法
void *objc_destructInstance(id obj) 
{
    if (obj) {
        // Read all of the flags at once for performance.
        // 判斷是否有C++析構(gòu)函數(shù)
        bool cxx = obj->hasCxxDtor();
        // 是否有關(guān)聯(lián)對(duì)象
        bool assoc = obj->hasAssociatedObjects();

        // This order is important.
        // 清除有C++析構(gòu)函數(shù)
        if (cxx) object_cxxDestruct(obj);
        // 清除關(guān)聯(lián)對(duì)象表 AssociationsManager對(duì)應(yīng)的value
        if (assoc) _object_remove_assocations(obj);
        obj->clearDeallocating();
    }
    return obj;
}
  • 最后調(diào)用了obj->clearDeallocating()可以自己進(jìn)去看看,思維導(dǎo)圖里面注釋的比較清楚如圖:


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

// 判斷是否有C++析構(gòu)函數(shù)
bool cxx = obj->hasCxxDtor();
// 是否有關(guān)聯(lián)對(duì)象
bool assoc = obj->hasAssociatedObjects();

  • 可以根據(jù)isa指針聯(lián)合體里面有記錄的禁舷,大致如下:
union isa_t 
struct {
    // 1代表優(yōu)化后的使用位域存儲(chǔ)更多的信息派近。
    uintptr_t nonpointer        : 1; 

   // 是否有設(shè)置過(guò)關(guān)聯(lián)對(duì)象
    uintptr_t has_assoc         : 1;

    // 是否有C++析構(gòu)函數(shù)
    uintptr_t has_cxx_dtor      : 1;

    // 存儲(chǔ)著Class對(duì)象的內(nèi)存地址信息
    uintptr_t shiftcls          : 33; 

    // 用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
    uintptr_t magic             : 6;

    // 是否有被弱引用指向過(guò)战坤。
    uintptr_t weakly_referenced : 1;

    // 對(duì)象是否正在釋放
    uintptr_t deallocating      : 1;

    // 引用計(jì)數(shù)器是否過(guò)大無(wú)法存儲(chǔ)在isa中
    // 如果為1碟嘴,那么引用計(jì)數(shù)會(huì)存儲(chǔ)在一個(gè)叫SideTable的類(lèi)的屬性中
    uintptr_t has_sidetable_rc  : 1;

    // 里面存儲(chǔ)的值是引用計(jì)數(shù)器減1
    uintptr_t extra_rc          : 19;
};
  • 今天就到這了栅组,小子頭昏眼花刃麸,急需按一波。陸續(xù)會(huì)更新內(nèi)存管理的內(nèi)容,各位看官,回見(jiàn)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邦蜜,死亡現(xiàn)場(chǎng)離奇詭異依鸥,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)悼沈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)贱迟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人絮供,你說(shuō)我怎么就攤上這事衣吠。” “怎么了壤靶?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蒸播,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我萍肆,道長(zhǎng),這世上最難降的妖魔是什么胀屿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任塘揣,我火速辦了婚禮,結(jié)果婚禮上宿崭,老公的妹妹穿的比我還像新娘亲铡。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布奖蔓。 她就那樣靜靜地躺著赞草,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吆鹤。 梳的紋絲不亂的頭發(fā)上厨疙,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音疑务,去河邊找鬼沾凄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛知允,可吹牛的內(nèi)容都是我干的撒蟀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼温鸽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼保屯!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起涤垫,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤姑尺,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后雹姊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體股缸,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年吱雏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敦姻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歧杏,死狀恐怖镰惦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情犬绒,我是刑警寧澤旺入,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站凯力,受9級(jí)特大地震影響茵瘾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咐鹤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一拗秘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧祈惶,春花似錦雕旨、人聲如沸扮匠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棒搜。三九已至,卻和暖如春活箕,著一層夾襖步出監(jiān)牢的瞬間力麸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工讹蘑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留末盔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓座慰,卻偏偏與公主長(zhǎng)得像陨舱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子版仔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容