一、底层研究的三种方法(查找底层源码出处)
-
下符号断点
断点断住[LGPerson alloc];
我们要研究alloc方法所以直接下一个alloc的符号断点(第二种方法详细讲了下符号断点的方法)、然后点击continue
这时候会进入alloc的符号断点,可以看到alloc方法是在libobjc.A.dylib库中
ps:也可以一开始就下alloc的符号断点,但是要先设置成Disable 等到了[LGPerson alloc];断点之后再把alloc的符号断点设置成Enable因为系统也会调用alloc方法我们要避免干扰准确的断住LGPerson的alloc
-
按住control + step into进入源码出处
下断点断住[LGPerson alloc]、然后按住control + step into进入源码
可以看到底层实际上是进入了objc_alloc方法里
添加一个objc_alloc的符号断点,然后点击continue
可以看到objc_alloc方法是在libobjc.A.dylib库中
-
汇编跟流程查看实际调用的符号下符号断点
设置Xcode ->Debug ->Debug Workflow->Always Show Disaassemdly显示汇编代码
可以看到实际调用的是objc_alloc符号 接下来下个对应的符号断点,就可以断点objc_alloc可以看到源码的出处
源码下载地址
苹果开源源码汇总: https://opensource.apple.com
这个地址⽤的更直接 https://opensource.apple.com/tarballs/
进入苹果开源网站点击macOS下的版本能看到苹果开源的源码库 搜索objc下载对应的源码
二、探究alloc源码
可以看到p1、p2、p3指向同一个对象(指向的内存地址也是一样的)三个指针自身的地址不同
PS:p1、p2、p3是在栈空间里的三个指针、他们的内存不同、他们指向堆空间里同一个地址LGPerson对象
接下来通过源码探究alloc 和 init做了些啥
在源码里搜索alloc的实现可以看到是在NSObject.mm(OC、C、C++混编)里调用了_objc_rootAlloc方法,一路找下去
_objc_rootAlloc调用了callAlloc
callAlloc里通过cls->ISA()->hasCustomAWZ()当前class是否有自定义的allocWithZone,如果没有自定义会调用_objc_rootAllocWithZone
而_objc_rootAllocWithZone最终会调用_class_createInstanceFromZone这个方法里最终生成对象,这里先理清楚流程后面会具体分析这个方法
callAlloc里如果自定义的allocWithZone,会判断当前类是否可以快速alloc如果没有会通过objc_msgSend调用allocWithZone:方法
如果可以快速alloc会通过objc_msgSend调用alloc方法
根据这个流程得到下面的流程图
可以看到最终都是通过_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构造方法的实现。
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析构方法的实现。
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结构体中,然后经过对齐最后返回(对象的内存大小8字节对齐,这样可以提高cpu效率)。
注意:Core Foundation 需要所有的对象的大小都必须大于或等于 16 字节。
在获取对象大小之后,直接调用calloc函数就可以为对象分配内存空间了。
关于calloc函数
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()函数连续地为count对象分配足够的空间,这些对象是内存的大小字节,并返回一个指向所分配内存的指针。分配的内存充满了值为零的字节。
申请完内存空间之后,还需要再初始化Isa指针。
obj->initInstanceIsa(cls, hasCxxDtor);
obj->initIsa(cls);
初始化Isa指针有这上面两个函数。
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);
}
从上述源码中,我们也能看出,最终都是调用了initIsa函数,只不过入参不同。
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结构体初始化的过程。
初始化之后obj被ruturn给外界使用alloc流程结束
待补充内容:
1、编译器优化
2、对象的字节对齐
3、内存对齐
4、对象大小的影响因素
参考资料:OC对象的一生