一、底層研究的三種方法(查找底層源碼出處)
-
下符號斷點
斷點斷住[LGPerson alloc];
1624431409030.png
我們要研究alloc方法所以直接下一個alloc的符號斷點(第二種方法詳細講了下符號斷點的方法)、然后點擊continue

這時候會進入alloc的符號斷點,可以看到alloc方法是在libobjc.A.dylib庫中
ps:也可以一開始就下alloc的符號斷點,但是要先設(shè)置成Disable 等到了[LGPerson alloc];斷點之后再把alloc的符號斷點設(shè)置成Enable因為系統(tǒng)也會調(diào)用alloc方法我們要避免干擾準確的斷住LGPerson的alloc
-
按住control + step into進入源碼出處
1624430823944.png
下斷點斷住[LGPerson alloc]、然后按住control + step into進入源碼

可以看到底層實際上是進入了objc_alloc方法里


添加一個objc_alloc的符號斷點,然后點擊continue

可以看到objc_alloc方法是在libobjc.A.dylib庫中
-
匯編跟流程查看實際調(diào)用的符號下符號斷點
1624432052303.png
設(shè)置Xcode ->Debug ->Debug Workflow->Always Show Disaassemdly顯示匯編代碼

可以看到實際調(diào)用的是objc_alloc符號 接下來下個對應(yīng)的符號斷點,就可以斷點objc_alloc可以看到源碼的出處
源碼下載地址
蘋果開源源碼匯總: https://opensource.apple.com
這個地址?的更直接 https://opensource.apple.com/tarballs/


進入蘋果開源網(wǎng)站點擊macOS下的版本能看到蘋果開源的源碼庫 搜索objc下載對應(yīng)的源碼
二、探究alloc源碼

可以看到p1、p2、p3指向同一個對象(指向的內(nèi)存地址也是一樣的)三個指針自身的地址不同
PS:p1、p2、p3是在??臻g里的三個指針、他們的內(nèi)存不同、他們指向堆空間里同一個地址LGPerson對象
接下來通過源碼探究alloc 和 init做了些啥

在源碼里搜索alloc的實現(xiàn)可以看到是在NSObject.mm(OC、C、C++混編)里調(diào)用了_objc_rootAlloc方法,一路找下去

_objc_rootAlloc調(diào)用了callAlloc

callAlloc里通過cls->ISA()->hasCustomAWZ()當前class是否有自定義的allocWithZone,如果沒有自定義會調(diào)用_objc_rootAllocWithZone

而_objc_rootAllocWithZone最終會調(diào)用_class_createInstanceFromZone這個方法里最終生成對象,這里先理清楚流程后面會具體分析這個方法

callAlloc里如果自定義的allocWithZone,會判斷當前類是否可以快速alloc如果沒有會通過objc_msgSend調(diào)用allocWithZone:方法
如果可以快速alloc會通過objc_msgSend調(diào)用alloc方法
根據(jù)這個流程得到下面的流程圖

可以看到最終都是通過_class_createInstanceFromZone方法生成的對象接下來就來看下這個方法
/***********************************************************************
- class_createInstance
- fixme
- Locking: none
- Note: this function has been carefully written so that the fastpath
- takes no branch.
**********************************************************************/
static ALWAYS_INLINE id
_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());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
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) {
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);
}
ctor 和 dtor 分別是什么呢?
bool hasCxxCtor() {
ASSERT(isRealized());
return cache.getBit(FAST_CACHE_HAS_CXX_CTOR);
}
bool hasCxxCtor() {
ASSERT(isRealized());
return bits.data()->flags & RW_HAS_CXX_CTOR;
}
ctor是判斷當前class或者superclass 是否有.cxx_construct構(gòu)造方法的實現(xiàn)。
bool hasCxxDtor() {
ASSERT(isRealized());
return cache.getBit(FAST_CACHE_HAS_CXX_DTOR);
}
bool hasCxxDtor() {
ASSERT(isRealized());
return bits.data()->flags & RW_HAS_CXX_DTOR;
}
dtor是判斷判斷當前class或者superclass 是否有.cxx_destruct析構(gòu)方法的實現(xiàn)。
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
實例大小 instanceSize會存儲在類的 isa_t結(jié)構(gòu)體中,然后經(jīng)過對齊最后返回(對象的內(nèi)存大小8字節(jié)對齊,這樣可以提高cpu效率)。
注意:Core Foundation 需要所有的對象的大小都必須大于或等于 16 字節(jié)。
在獲取對象大小之后,直接調(diào)用calloc函數(shù)就可以為對象分配內(nèi)存空間了。
關(guān)于calloc函數(shù)
The calloc( ) function contiguously allocates enough space for count objects that are size bytes of memory each and returns a pointer to the allocated memory. The allocated memory is filled with bytes of value zero. // calloc()函數(shù)連續(xù)地為count對象分配足夠的空間,這些對象是內(nèi)存的大小字節(jié),并返回一個指向所分配內(nèi)存的指針。分配的內(nèi)存充滿了值為零的字節(jié)。
申請完內(nèi)存空間之后,還需要再初始化Isa指針。
obj->initInstanceIsa(cls, hasCxxDtor);
obj->initIsa(cls);
初始化Isa指針有這上面兩個函數(shù)。
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
從上述源碼中,我們也能看出,最終都是調(diào)用了initIsa函數(shù),只不過入?yún)⒉煌?/p>
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
if (!nonpointer) {
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#if SUPPORT_INDEXED_ISA
ASSERT(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
初始化的過程就是對isa_t結(jié)構(gòu)體初始化的過程。
初始化之后obj被ruturn給外界使用alloc流程結(jié)束
待補充內(nèi)容:
1、編譯器優(yōu)化
2、對象的字節(jié)對齊
3、內(nèi)存對齊
4、對象大小的影響因素
參考資料:OC對象的一生


