一培己、 OC的alloc初探
代碼準(zhǔn)備巾乳,我們先對(duì)一個(gè)類alloc一個(gè)對(duì)象出來(lái)
NSObject *objc = [NSObject alloc];
在這里下好斷點(diǎn)主穗,打開匯編調(diào)試(Debug->Debug workflow->Always Show Disassembly)
運(yùn)行茎刚,
我們可以看到下一條指令的匯編注釋symbol stub for: objc_alloc
得知立即調(diào)用存根符號(hào)為objc_alloc
的函數(shù)悲关,所以我們不妨增加一個(gè)objc_alloc
的符號(hào)斷點(diǎn)谎僻,接著運(yùn)行可以看到
所以可以看到在調(diào)用NSObject *objc = [NSObject alloc];
時(shí),其實(shí)調(diào)用的是objc
中的objc_alloc
方法坚洽。
二戈稿、OC 的 alloc 深入探究
經(jīng)過(guò)上面一步,我們就能淺嘗輒止嗎讶舰?當(dāng)然不能鞍盗;所以我搞來(lái)一份objc4源碼繼續(xù)我們的探究。
看到源碼 NSObject.mm 文件跳昼,里面的alloc方法
他會(huì)調(diào)用
_objc_rootAlloc
- 問(wèn)題:
我上面看到 [NSObject alloc]看到的不是會(huì)到objc_alloc來(lái)嗎般甲?怎么源碼分析的到了_objc_rootAlloc? - 答:我們知道OC代碼有運(yùn)行時(shí)的特點(diǎn)鹅颊,即方法的調(diào)用不是在編譯時(shí)確定的敷存,而是運(yùn)行時(shí),不懂的補(bǔ)一下編譯原理堪伍。我們?cè)趏bjc運(yùn)行時(shí)源碼中查找objc_alloc锚烦,發(fā)現(xiàn)如下的代碼
static void
fixupMessageRef(message_ref_t *msg)
{
msg->sel = sel_registerName((const char *)msg->sel);
if (msg->imp == &objc_msgSend_fixup) {
if (msg->sel == @selector(alloc)) {
msg->imp = (IMP)&objc_alloc;
} else if (msg->sel == @selector(allocWithZone:)) {
msg->imp = (IMP)&objc_allocWithZone;
} else if (msg->sel == @selector(retain)) {
msg->imp = (IMP)&objc_retain;
} else if (msg->sel == @selector(release)) {
msg->imp = (IMP)&objc_release;
} else if (msg->sel == @selector(autorelease)) {
msg->imp = (IMP)&objc_autorelease;
} else {
msg->imp = &objc_msgSend_fixedup;
}
}
else if (msg->imp == &objc_msgSendSuper2_fixup) {
msg->imp = &objc_msgSendSuper2_fixedup;
}
else if (msg->imp == &objc_msgSend_stret_fixup) {
msg->imp = &objc_msgSend_stret_fixedup;
}
else if (msg->imp == &objc_msgSendSuper2_stret_fixup) {
msg->imp = &objc_msgSendSuper2_stret_fixedup;
}
#if defined(__i386__) || defined(__x86_64__)
else if (msg->imp == &objc_msgSend_fpret_fixup) {
msg->imp = &objc_msgSend_fpret_fixedup;
}
#endif
#if defined(__x86_64__)
else if (msg->imp == &objc_msgSend_fp2ret_fixup) {
msg->imp = &objc_msgSend_fp2ret_fixedup;
}
#endif
}
發(fā)現(xiàn)罅隙發(fā)送時(shí),當(dāng)msg->imp == &objc_msgSend_fixup
進(jìn)行了方法實(shí)現(xiàn)(IMP)的替換帝雇。
什么時(shí)候
msg->imp == &objc_msgSend_fixup
成立還沒有確定的探究涮俄,后續(xù)了解會(huì)進(jìn)一步更新。從源碼的運(yùn)行看[NSObject alloc]
會(huì)走到objc_alloc
尸闸;但是其子類的[LGPerson alloc]
會(huì)調(diào)用_objc_rootAlloc
不管是_objc_rootAlloc
或者objc_alloc
從圖4看都會(huì)進(jìn)入callAlloc
這個(gè)函數(shù),不過(guò)最后兩個(gè)參數(shù)不同彻亲;我們姑且往下走,進(jìn)入callAlloc
去看看究竟。代碼如下
- 這里看到有兩個(gè)宏定義函數(shù):
slowpath
,fastpath
. 這兩個(gè)函數(shù)是用于編譯器優(yōu)化吮廉;slowpath
低概率會(huì)走;fastpath
大概率會(huì)走,如果項(xiàng)目中有需求苞尝,自己也可以用上,讓自己的代碼質(zhì)量提高宦芦。 -
hasCustomAWZ
是否有自定義的AWZ
宙址;經(jīng)過(guò)運(yùn)行實(shí)際得出,當(dāng)類的第一次運(yùn)行時(shí)slowpath
踪旷,fastpath
判斷都為false,后面在運(yùn)行時(shí)fastpath
為true;不過(guò)我們看到下面的函數(shù)又是消息的發(fā)送曼氛,調(diào)用的是alloc
和allocWithZone
豁辉,其實(shí)接著往下走,可以發(fā)現(xiàn)舀患,他又回到了callAlloc
,并且此時(shí)的fastpath
為true;
那么函數(shù)此時(shí)應(yīng)來(lái)到_objc_rootAllocWithZone
unsigned
class_createInstances(Class cls, size_t extraBytes,
id *results, unsigned num_requested)
{
return _class_createInstancesFromZone(cls, extraBytes, nil,
results, num_requested);
}
那么我們?nèi)タ纯?code>_class_createInstancesFromZone
unsigned
_class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone,
id *results, unsigned num_requested)
{
unsigned num_allocated;
if (!cls) return 0;
size_t size = cls->instanceSize(extraBytes);
num_allocated =
malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()),
size, (void**)results, num_requested);
for (unsigned i = 0; i < num_allocated; i++) {
bzero(results[i], size);
}
// Construct each object, and delete any that fail construction.
unsigned shift = 0;
bool ctor = cls->hasCxxCtor();
for (unsigned i = 0; i < num_allocated; i++) {
id obj = results[i];
obj->initIsa(cls); // fixme allow nonpointer
if (ctor) {
obj = object_cxxConstructFromClass(obj, cls,
OBJECT_CONSTRUCT_FREE_ONFAILURE);
}
if (obj) {
results[i-shift] = obj;
} else {
shift++;
}
}
return num_allocated - shift;
}
這里的邏輯大體分為
- 1徽级、求出實(shí)例所需內(nèi)存大小
size_t size = cls->instanceSize(extraBytes);
- 2、分配空間
num_allocated =
malloc_zone_batch_malloc((malloc_zone_t *)(zone ? zone : malloc_default_zone()),
size, (void**)results, num_requested);
3聊浅、綁定isa到類
obj->initIsa(cls); // fixme allow nonpointer
接下來(lái)返回對(duì)應(yīng)類的首地址
三餐抢、總結(jié)
NSObject 類
alloc具體流程
alloc ==> objc_alloc ==> callAlloc ==> _objc_rootAllocWithZone ==> _class_createInstanceFromZone ==>(1、獲取實(shí)例大小size;2低匙、分配size;3旷痕、initInstanceIsa)NSObject 繼承類
alloc具體流程
alloc ==> _objc_rootAlloc ==> callAlloc ==> objc_msgSend ==> _objc_rootAllocWithZone ==> _class_createInstanceFromZone ==>(1、獲取實(shí)例大小size;2顽冶、分配size;3欺抗、initInstanceIsa)