本系列博客是本人的源码阅读笔记,如果有 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壁纸宝贝上线了,欢迎大家下载。