Pre
提出问题
首先我们有如下代码
YKPerson *p0 = [YKPerson alloc];
YKPerson *p1 = [p0 init];
YKPerson *p2 = [p0 init];
NSLog(@"%@-%p-%p",p0,p0,&p0);
NSLog(@"%@-%p-%p",p1,p1,&p1);
NSLog(@"%@-%p-%p",p2,p2,&p2);
复制代码
根据打印结果可以看出指向的内存地址空间是一样的,但指针存储在栈中,且为连续存储,每个相隔8字节。
流程分析
我们从NSObject
的alloc
方法入手,探索对象创建的流程。从下面的代码调用来看,会走到callAlloc
方法。
+ (id)alloc {
return _objc_rootAlloc(self); // self -> YKPerson
}
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id
callAlloc(Class cls, // YKPerson
bool checkNil, // false
bool allocWithZone=false) // true
{
···
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
···
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
···
}
复制代码
但实际上在llvm_read_images
的过程中,会调用到fixupMessageRef
这个函数,在这其中则对alloc
的调用进行了重定向
if (msg->sel == @selector(alloc)) {
msg->imp = (IMP)&objc_alloc;
}
复制代码
也就是说当我们调用alloc
,实际调用的是objc_alloc
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
复制代码
callAlloc
上面的判断中我们可以梳理一下在callAlloc
方法中的判断流程。
判断条件
fastpath(!cls->ISA()->hasCustomAWZ())
复制代码
结果为true
根据代码的调用流程可以看到会走到_objc_rootAllocWithZone
方法
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
复制代码
结果为false
根据代码的调用流程可以看到会走到_objc_rootAllocWithZone
方法
return _objc_rootAllocWithZone(cls, nil);
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// 见下面的详细分析
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
复制代码
【核心方法】_class_createInstanceFromZone
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, // YKPerson
size_t extraBytes, // 0
void *zone, // nil
int construct_flags = OBJECT_CONSTRUCT_NONE, // OBJECT_CONSTRUCT_CALL_BADALLOC
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
复制代码
基本条件判断
ASSERT(cls->isRealized());
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor(); // 是否有C++构造函数
bool hasCxxDtor = cls->hasCxxDtor(); // 是否有C++析构函数
bool fast = cls->canAllocNonpointer(); // 是否可以分配
复制代码
获取cls的实例所需占用的内存大小
size_t size;
size = cls->instanceSize(extraBytes); // extraBytes == 0
复制代码
- 如果之前已经获取过实例大小,从缓存中取值返回。
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
复制代码
取值时会先判断__builtin_constant_p(extra) && extra == 0
/*
* true
*/
return _flags & FAST_CACHE_ALLOC_MASK16;
/*
* false
*/
size_t size = _flags & FAST_CACHE_ALLOC_MASK; // #define FAST_CACHE_ALLOC_MASK 0x1ff8
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16); // #define FAST_CACHE_ALLOC_DELTA16 0x0008 即align16()为16字节对齐
复制代码
关于
__builtin_constant_p
,查过资料后知道这是编译器内置函数。它接受一个数值参数,如果已知参数是一个编译时常量,则返回 1。返回值 0 意味着编译器无法确定参数是否是编译时常量。此内置函数的典型用法是在宏中用于手动编译时优化。
- 如果没有,则计算出值。
else {
size_t size = alignedInstanceSize() + extraBytes;
}
复制代码
- 若size小于16,出于【内存补齐】的原则,补齐16个字节
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
复制代码
开辟内存空间,返回地址指针
id obj;
obj = (id)calloc(1, size);
// ->
void *calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
复制代码
将内存地址与类绑定
obj->initInstanceIsa(cls, hasCxxDtor);
// ->
initIsa(cls, false, false);
复制代码
inline void
objc_object::initIsa(Class cls, // YKPerson
bool nonpointer, // false
bool hasCxxDtor) // false
{
isa_t newisa(0)
···
newisa.setClass(cls, this);
···
isa = newisa;
}
复制代码
- 创建一个联合体位域结构
isa_t
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(**bool** authenticated);
Class getDecodedClass(**bool** authenticated);
};
复制代码
- 绑定
inline void
isa_t::setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_NONE
uintptr_t signedCls = (uintptr_t)newCls;
# elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ONLY_SWIFT
uintptr_t signedCls = (uintptr_t)newCls;
if (newCls->isSwiftStable())
signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));
# elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL
uintptr_t signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));
# else
# error Unknown isa signing mode.
# endif
shiftcls_and_sig = signedCls >> 3;
#elif SUPPORT_INDEXED_ISA
cls = newCls;
#else
shiftcls = (uintptr_t)newCls >> 3;
#endif
}
复制代码
补充
内存对齐
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END