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)存分布
至此,我們的探索流程就告一段落了藕漱。