参考文档:
- 对象销毁过程:
Objc对象的销毁过程
iOS引用计数
对象生命周期由引用计数器的概念管理,每次对象失去一个被引用关系,会调用对应的release操作做引用计数器减1操作,当对象的引用计数为0时,会走真正的对象释放流程,走到dealloc释放方法;
引用计数器-对象调用release
objc_object::release()
{
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
sidetable_release();
return;
}
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
}
bjc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
assert(!isa.nonpointer);
#endif
SideTable& table = SideTables()[this];
bool do_dealloc = false;
//加锁
table.lock();
//获取当前对象所在的sidetable(一个hash表),在sidetable.refcnts(RefcountMap,一个map)中查到当前对象的迭代器
RefcountMap::iterator it = table.refcnts.find(this);
//接着判断迭代器是否是指向了sidetable的end
//如果是就代表找不到:
if (it == table.refcnts.end()) {
//将对象标记为“正在析构”
//标记需要dealloc
do_dealloc = true;
table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
} else if (it->second < SIDE_TABLE_DEALLOCATING) {
//判断之前存储的引用计数值是否为 0,避免负数
//将对象标记为“正在析构”
//标记需要dealloc
// SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
do_dealloc = true;
it->second |= SIDE_TABLE_DEALLOCATING;
} else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
//将引用计数减一
it->second -= SIDE_TABLE_RC_ONE;
}
//解锁
table.unlock();
if (do_dealloc && performDealloc) {
//调用对象的dealloc方法:
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return do_dealloc;
}
dealloc调用流程
NSObject的dealloc会调用
_objc_rootDealloc(self)
;
- (void)dealloc {
_objc_rootDealloc(self);
}
_objc_rootDealloc(id obj)
{
//是否还活着 断言
assert(obj);
//调用 objc_object::rootDealloc()
obj->rootDealloc();
}
objc_object::rootDealloc()
{
//是否使用TaggedPointer优化 是直接返回
if (isTaggedPointer()) return; // fixme necessary?
//不需要处理object_dispose的所有内容
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
//调用object_dispose
object_dispose((id)this);
}
}
id object_dispose(id obj)
{
//是否为nil 直接返回
if (!obj) return nil;
//调用 void *objc_destructInstance(id obj)
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
//如果不为nil 才处理
if (obj) {
//是否有析构函数 这个bool值取决于当前类以及父类往上是否有实例变量,如果有实例变量当前类就有.cxxDestruct,当前类或父类有此方法值=YES,都没有才=NO
bool cxx = obj->hasCxxDtor();
//是否有关联对象
bool assoc = obj->hasAssociatedObjects();
// This order is important.
//如果有析构函数 调用 void object_cxxDestruct(id obj)
if (cxx) object_cxxDestruct(obj);
//如果有关联对象 移除关联对象
if (assoc) _object_remove_assocations(obj);
//调用objc_clear_deallocating()清空引用计数表
obj->clearDeallocating();
}
return obj;
}
析构函数
void object_cxxDestruct(id obj)
{
//如果为nil 直接retun
if (!obj) return;
//是否使用TaggedPointer优化 是直接return
if (obj->isTaggedPointer()) return;
//调用 static void object_cxxDestructFromClass(id obj, Class cls)
object_cxxDestructFromClass(obj, obj->ISA());
}
static void object_cxxDestructFromClass(id obj, Class cls)
{
void (*dtor)(id);
// Call cls's dtor first, then superclasses's dtors.
//往父类递归调用.cxxDestruct 直到hasCxxDtor=NO return结束
for ( ; cls; cls = cls->superclass) {
if (!cls->hasCxxDtor()) return;
// 在类和缓存中查找方法
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
// 查找到cxx_destruct方法(SEL_cxx_destruct = sel_registerNameNoLock(".cxx_destruct", NO);)
if (dtor != (void(*)(id))_objc_msgForward_impcache) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
cls->nameForLogging());
}
(*dtor)(obj);
}
}
}
.cxx_destruct方法原本是为了C++对象析构的,ARC借用了这个方法插入代码实现了自动内存释放的工作
关联对象
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
关联对象由一个单独单独的管理类进行统一管理,维护有一个哈希表AssociationsHashMap
,key为obj,value为一个哈希表ObjectAssociationMap
,存储着关联的属性:值ObjcAssociation
;可以理解为两层哈希表;
{
obj:{
name:value,
age:value
}
}
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
struct ReleaseValue {
void operator() (ObjcAssociation &association) {
releaseValue(association.value(), association.policy());
}
};
static void releaseValue(id value, uintptr_t policy) {
if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
return objc_release(value);
}
}
清空引用计数表
void
objc_clear_deallocating(id obj)
{
assert(obj);
if (obj->isTaggedPointer()) return;
obj->clearDeallocating();
}
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
//调用sidetable_clearDeallocating()把对象的weak指针置nil,把对象的计数引用移除
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
/判断是否有过弱引用 是否因为计数太大有多个sidetable
// Slow path for non-pointer isa with weak refs and/or side table data.
//调用clearDeallocating_slow();内部再分开判断各自实现sidetable_clearDeallocating的内容
clearDeallocating_slow();
}
assert(!sidetable_present());
}
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
// weak指针置nil,
weak_clear_no_lock(&table.weak_table, (id)this);
}
// 把对象的计数引用移除
table.refcnts.erase(it);
}
table.unlock();
}
部分关键点注解
Tagged Pointer介绍
从64bit开始,iOS引入Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象存储。指针并没有指向真正的内容存储在指针中;这类对象没有引用计数器内存管理流程。
# define _OBJC_TAG_MASK (1UL<<63)
_objc_isTaggedPointer(const void * _Nullable ptr)
{
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
nonpointer介绍
在支持nonpointer的对象中引用计数会又选存储在extra_rc中,isa位域extra_rc溢出,则会选择将将引用计数的RC_HALF(如果extra_rc占据8bit,则RC_HALF=2^7)保存在isa中,另一半RC_HALF叠加保存在sidetable中.之所以这样选择是因为isa_t的存取理论上会比sidetable的操作效率上快很多,这样做既可以使超出extra_rc存储范围的引用计数得到有效存储,又可以确保引用计数的增减足够快速(存取都以extra_rc优先)。
1、在开启nonpointer的对象中,对象的引用计数包括两部分:
1)存储在isa中的引用计数(isa.extra_rc);
2)存储在sidetable中的引用计数.
2、在未开启nonpointer的对象中,对象的引用计数全部存储在sidetable中,只需要从sidetable中获取就可以.
isa_t结构(arm64)
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
fastpath、slowpath
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
__builtin_expect指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。
__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
__builtin_expect((x),1)表示 x 的值为真的可能性更大;
__builtin_expect((x),0)表示 x 的值为假的可能性更大。
也就是说,使用fastpath(),执行 if 后面的语句的机会更大,使用 slowpath(),执行 else 后面的语句的机会更大。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着前面的代码,从而减少指令跳转带来的性能上的下降。
.cxx_desctruct
参考链接:
- 越狱研究点滴---ARC下dealloc过程及.cxx_destruct的探究
ARC下对象的成员变量于编译器插入的.cxx_desctruct方法自动释放;
ARC下[super dealloc]方法也由编译器自动插入;