本系列博客是本人的源碼閱讀筆記,如果有 iOS 開發者在看 runtime 的,歡迎大家多多交流。為了方便討論,本人新建了一個微信群(iOS技術討論群),想要加入的,請添加本人微信:zhujinhui207407,【加我前請備注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,歡迎一起討論
本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime
前言
上篇文章中我們有提到,alloc函數在一定條件下最終會調用callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
方法,而該方法中有一段這樣的判斷條件(以下稱為邏輯A):
if (fastpath(!cls->ISA()->hasCustomAWZ()))
意思就是如果該類實現了allocWithZone方法,那么就不會走if里的邏輯,直接走以下邏輯(以下稱為邏輯B):
if (allocWithZone) return [cls allocWithZone:nil];
為了驗證這個判斷,筆者做了個實驗,在main.m
中鍵入以下代碼:
@interface Person :NSObject
@end
@implementation Person
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
return nil;
}
@end
int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];
return 0;
}
會發現走邏輯B。那大家肯定能猜到,肯定是什么時候調用了某個函數,導致cls->ISA()->hasCustomAWZ()
變成了true。本文將帶大家了解CustomAWZ這個“屬性”設置的過程。
分析
首先 我們了解一下這段代碼:
cls->ISA()->hasCustomAWZ()
可知,這段代碼分兩步:
cls->ISA()
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
由前面的文章分析可知,該函數可以簡寫為
inline Class
objc_object::ISA()
{
assert(!isTaggedPointer());
return (Class)(isa.bits & ISA_MASK);
}
因此,這段代碼有是我們熟悉的,拿到了isa_t
的shiftcls
位域,改位域存儲了對象的內存區域。
-
hasCustomAWZ()
查看其實現,可以發現如下代碼:
bool hasCustomAWZ() {
return ! bits.hasDefaultAWZ();
}
查看其附近的函數,可以看到如下幾個函數:
#if FAST_HAS_DEFAULT_AWZ
bool hasDefaultAWZ() {
return getBit(FAST_HAS_DEFAULT_AWZ);
}
void setHasDefaultAWZ() {
setBits(FAST_HAS_DEFAULT_AWZ);
}
void setHasCustomAWZ() {
clearBits(FAST_HAS_DEFAULT_AWZ);
}
#else
bool hasDefaultAWZ() {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}
void setHasDefaultAWZ() {
data()->setFlags(RW_HAS_DEFAULT_AWZ);
}
void setHasCustomAWZ() {
data()->clearFlags(RW_HAS_DEFAULT_AWZ);
}
#endif
其中,宏 FAST_HAS_DEFAULT_AWZ
在文件objc-runtime-new.h
中有定義:
// objc-runtime-new.h
// Values for class_rw_t->flags or class_t->bits
// These flags are optimized for retain/release and alloc/dealloc
// 64-bit stores more of them in class_t->bits to reduce pointer indirection.
#if !__LP64__
...
#elif 1
...
#else
// summary bit for fast alloc path: !hasCxxCtor and
// !instancesRequireRawIsa and instanceSize fits into shiftedSize
// hasCxxCtor是判斷當前class或者superclass 是否有.cxx_construct構造方法的實現。
// FAST_ALLOC means
// FAST_HAS_CXX_CTOR is set
// FAST_REQUIRES_RAW_ISA is not set
// FAST_SHIFTED_SIZE is not zero
// FAST_ALLOC does NOT check FAST_HAS_DEFAULT_AWZ because that
// bit is stored on the metaclass.
#define FAST_ALLOC (1UL<<50)
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_HAS_DEFAULT_AWZ (1UL<<48)
#end
首先 if !__ LP64 __ 是處理32位系統的,這里暫時不考慮,然后這里需要注意的是 elif 1,就是else if(1) 的簡寫!
也就是說,#else 不會被編譯了,那么上面兩個條件 FAST_ALLOC 和 FAST_HAS_DEFAULT_AWZ就不成立。
因此以上代碼可以簡寫為:
bool hasDefaultAWZ() {
return data()->flags & RW_HAS_DEFAULT_AWZ;
}
void setHasDefaultAWZ() {
data()->setFlags(RW_HAS_DEFAULT_AWZ);
}
void setHasCustomAWZ() {
data()->clearFlags(RW_HAS_DEFAULT_AWZ);
}
這里不對這幾個函數做深入分析了,只是給大家提供一個參考:是否setHasCustomAWZ
函數就是導致hasCustomAWZ
為true的原因呢?帶著這個疑問,我們全局搜一下setHasCustomAWZ
,終于在如下代碼中找到了答案(代碼位于文件objc-runtime-new.mm
1400多行):
if (supercls->hasCustomAWZ()) {
subcls->setHasCustomAWZ(true);
}
為了驗證筆者的想法,筆者添加了日志函數用于驗證:
if (0 == strncmp(subcls->nameForLogging(), "Person", 5)) {
printf("subcls:%s,superclass:%s hasCustomAllocZone:%d\n",subcls->nameForLogging(),supercls->nameForLogging(),subcls->ISA()->hasCustomAWZ());
}
if (supercls->hasCustomAWZ()) {
subcls->setHasCustomAWZ(true);
}
if (0 == strncmp(subcls->nameForLogging(), "Person", 5)) {
printf("=====subcls:%s,superclass:%s hasCustomAllocZone:%d\n",subcls->nameForLogging(),supercls->nameForLogging(),subcls->ISA()->hasCustomAWZ());
}
可以看到日志臺打印如下結果:
至此,終于驗證了筆者想法。
nameForLogging()方法是打印類名的方法,這里簡單了解一下即可。后面的文章將給于詳細的解釋。
總結
本文通過alloc
函數中調用的函數callAlloc(Class cls, bool checkNil, bool allocWithZone=false)的分支if (fastpath(!cls->ISA()->hasCustomAWZ()))
猜測allocWithZone
影響的是結構體class_rw_t
中的flags
字段。至于這個結構體的詳細分析以及flags
的含義,筆者會在稍后的文章中給出。
本文完整版詳見筆者小專欄:https://xiaozhuanlan.com/runtime
廣告
我的首款個人開發的APP壁紙寶貝上線了,歡迎大家下載。