詳細(xì)探究alloc到底干了什么?

1周蹭、怎么去探究alloc趋艘?

我們在xcode中按住command鍵點(diǎn)進(jìn)去就進(jìn)入了一下頁面

+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
\+ (instancetype)allocWithZone:(struct _NSZone *)zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
\+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
\- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
\- (void)finalize OBJC_DEPRECATED("Objective-C garbage collection is no longer supported");
\- (id)copy;
\- (id)mutableCopy;

到這個(gè)頁面之后,就再也無法進(jìn)去了凶朗,臣妾想啊瓷胧,但是要不到啊。

正確的方法是:

去蘋果的開源庫中去下載相應(yīng)的代碼棚愤,去做進(jìn)一步探究搓萧。附鏈接

蘋果開源庫

當(dāng)然這個(gè)也是太麻煩了。還需要各種配置才能運(yùn)行起來宛畦,像我們這么“勤快的小蜜蜂”瘸洛,當(dāng)然是找別人配置好的呀。比如我擼過來的次和,可以直接用了

源碼下載

2反肋、alloc的流程

源碼下載之后,我們直接最新的818運(yùn)行起來斯够,新建一個(gè)文件ELPerson囚玫。

我們斷點(diǎn)先斷上,待他停住之后读规,我們直接上圖抓督。

#import <Foundation/Foundation.h>
#import "ELPerson.h"
int main(int argc, const char * argv[]) {
  @autoreleasepool {
    // insert code here...
    NSLog(@"Hello, World!");
    ELPerson *per = [ELPerson alloc];
    NSLog(@"%@",per);
     
  }
  return 0;
}

然后,我們摁住command+Step into束亏,這樣就進(jìn)去了铃在。

libobjc.A.dylib`+[NSObject alloc]:
  0x10033dd60 <+0>: pushq %rbp
  0x10033dd61 <+1>: movq  %rsp, %rbp
  0x10033dd64 <+4>: subq  $0x10, %rsp
  0x10033dd68 <+8>: movq  %rdi, -0x8(%rbp)
  0x10033dd6c <+12>: movq  %rsi, -0x10(%rbp)
-> 0x10033dd70 <+16>: movq  -0x8(%rbp), %rdi
  0x10033dd74 <+20>: callq 0x1002d86f0        ; _objc_rootAlloc at NSObject.mm:1948
  0x10033dd79 <+25>: addq  $0x10, %rsp
  0x10033dd7d <+29>: popq  %rbp
  0x10033dd7e <+30>: retq  

進(jìn)去之后我們就來到這個(gè)頁面,這個(gè)最上面的意思就是,方法調(diào)用到了libobjc.A.dylib這個(gè)動態(tài)庫里面去了定铜,匯編代碼看不懂沒關(guān)系阳液,我們繼續(xù)往下看看。后面有個(gè)

callq 0x1002d86f0        ; _objc_rootAlloc at NSObject.mm:1948

很明顯調(diào)用_objc_rootAlloc方法去了揣炕。這樣我們就可以看到帘皿,方法調(diào)用的流程了。但是這個(gè)方法太不友好了畸陡。里面都是匯編鹰溜,操作麻煩。很容易打擊我們探索的積極性丁恭。所以我們直接摁住command找到alloc點(diǎn)進(jìn)去

+ (id)alloc {
  return _objc_rootAlloc(self);
}

我們繼續(xù)進(jìn)去

callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);

這樣就可以了 曹动,簡單易懂。遇到if()else()這樣的分支牲览,我們直接下斷點(diǎn)去分析就好了

前面的流程基本都是無意義的方法調(diào)用墓陈,我們直接找到

_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
              int construct_flags = OBJECT_CONSTRUCT_NONE,
               bool cxxConstruct = true,
               size_t *outAllocatedSize = nil)
{
  ASSERT(cls->isRealized());
  // 一次讀取類的信息以提高性能
  bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
  bool hasCxxDtor = cls->hasCxxDtor();
  bool fast = cls->canAllocNonpointer();
  size_t size;
  //計(jì)算要開辟類內(nèi)存大小
  size = cls->instanceSize(extraBytes);
  if (outAllocatedSize) *outAllocatedSize = size;
  id obj;
  if (zone) {
    obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
  } else {
    obj = (id)calloc(1, size);
  }
  if (slowpath(!obj)) {
    if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
      return _objc_callBadAllocHandler(cls);
    }
    return nil;
  }

  if (!zone && fast) {//關(guān)聯(lián)指針和類
    obj->initInstanceIsa(cls, hasCxxDtor);
  } else {
    // Use raw pointer isa on the assumption that they might be
    // doing something weird with the zone or RR.
    obj->initIsa(cls);
  }
  if (fastpath(!hasCxxCtor)) {
    return obj;
  }
  construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
  return object_cxxConstructFromClass(obj, cls, construct_flags);

}

我們點(diǎn)擊instanceSize()進(jìn)去之后看看實(shí)現(xiàn)

 inline size_t instanceSize(size_t extraBytes) const {
    if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
      return cache.fastInstanceSize(extraBytes);
    }
    size_t size = alignedInstanceSize() + extraBytes;
    // CF 要求所有對象至少為16個(gè)字節(jié)。
    if (size < 16) size = 16;
    return size;
  }

我們看看第献,這個(gè)地方已經(jīng)加入了緩存贡必,可能蘋果覺得不加緩存太慢了,在我們新的版本中加入了緩存痊硕。下面是一個(gè)字節(jié)對齊算法赊级。CF 要求所有對象至少為 16 個(gè)字節(jié)。

我們直接打印下岔绸,驗(yàn)證也可以看出來進(jìn)行了字節(jié)對齊

2021-06-07 12:37:11.197898+0800 KCObjcBuild[53421:1865440] <ELPerson: 0x1018100a0>**
**(lldb)** **po extraBytes**
88
**(lldb)** **po size**
96

再看下

size_t size = alignedInstanceSize() + extraBytes;

里面的實(shí)現(xiàn)是

  uint32_t alignedInstanceSize() const {
   return word_align(unalignedInstanceSize());
  }
 uint32_t unalignedInstanceSize() const 
    ASSERT(isRealized());
    return data()->ro()->instanceSize;
  }
//OC對象成員變量8字節(jié)對齊運(yùn)算
static inline uint32_t word_align(uint32_t x) {
  eturn (x + WORD_MASK) & ~WORD_MASK;
}

再繼續(xù)探索理逊,就來到開辟內(nèi)存

 obj = (id)calloc(1, size);

最后調(diào)用object_cxxConstructFromClass(),從基類開始遞歸調(diào)用盒揉,構(gòu)造成功就返回我們的對象晋被。

id 
object_cxxConstructFromClass(id obj, Class cls, int flags)
{
  ASSERT(cls->hasCxxCtor()); // required for performance, not correctness
  id (*ctor)(id);
  Class supercls;
  supercls = cls->getSuperclass();
  // Call superclasses' ctors first, if any.
  if (supercls && supercls->hasCxxCtor()) {
    bool ok = object_cxxConstructFromClass(obj, supercls, flags);
    if (slowpath(!ok)) return nil; // some superclass's ctor failed - give up
  }
  // Find this class's ctor, if any.
  ctor = (id(*)(id))lookupMethodInClassAndLoadCache(cls, SEL_cxx_construct);
  if (ctor == (id(*)(id))_objc_msgForward_impcache) return obj; // no ctor - ok
  // Call this class's ctor.
  if (PrintCxxCtors) {
    _objc_inform("CXX: calling C++ constructors for class %s", 

          cls->nameForLogging());
  }
  if (fastpath((*ctor)(obj))) return obj; // ctor called and succeeded - ok
  supercls = cls->getSuperclass(); // this reload avoids a spill on the stack
  // This class's ctor was called and failed.
  // Call superclasses's dtors to clean up.
  if (supercls) object_cxxDestructFromClass(obj, supercls);
  if (flags & OBJECT_CONSTRUCT_FREE_ONFAILURE) free(obj);
  if (flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
    return _objc_callBadAllocHandler(cls);
  }
  return nil;
}

3、去驗(yàn)證內(nèi)存

我們先給ELPerson添加幾個(gè)屬性刚盈,賦值之后斷點(diǎn)斷上羡洛,然后

(lldb)** **x per**

0x100c2c870: 75 82 00 00 01 80 1d 01 30 40 00 00 01 00 00 00 u.......0@......

0x100c2c880: 00 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 ................

也可以View Memory查看內(nèi)存分布

image
lldb

至此,我們的探索流程就告一段落了藕漱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欲侮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子肋联,更是在濱河造成了極大的恐慌威蕉,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橄仍,死亡現(xiàn)場離奇詭異韧涨,居然都是意外死亡牍戚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門虑粥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來如孝,“玉大人,你說我怎么就攤上這事娩贷〉谖” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵彬祖,是天一觀的道長但荤。 經(jīng)常有香客問我,道長涧至,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任桑包,我火速辦了婚禮南蓬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘哑了。我一直安慰自己赘方,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布弱左。 她就那樣靜靜地躺著窄陡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拆火。 梳的紋絲不亂的頭發(fā)上跳夭,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機(jī)與錄音们镜,去河邊找鬼币叹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛模狭,可吹牛的內(nèi)容都是我干的颈抚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嚼鹉,長吁一口氣:“原來是場噩夢啊……” “哼贩汉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锚赤,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤匹舞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后宴树,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體策菜,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了又憨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片翠霍。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蠢莺,靈堂內(nèi)的尸體忽然破棺而出寒匙,到底是詐尸還是另有隱情,我是刑警寧澤躏将,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布锄弱,位于F島的核電站,受9級特大地震影響祸憋,放射性物質(zhì)發(fā)生泄漏会宪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一蚯窥、第九天 我趴在偏房一處隱蔽的房頂上張望掸鹅。 院中可真熱鬧,春花似錦拦赠、人聲如沸巍沙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽句携。三九已至,卻和暖如春允乐,著一層夾襖步出監(jiān)牢的瞬間矮嫉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工牍疏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留敞临,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓麸澜,卻偏偏與公主長得像挺尿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子炊邦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

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