dealloc的释放颇具色彩:因为OC的结构,弱引用,关联对象,C++等,所以需要分开释放。
1. dealloc
//NSObject.mm
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
//objc-object.h
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
//开启了指针优化、没有弱引用计数、没有关联对象、没有C++、没有sidetabled的引用计数
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((id)this);//弱引用计数、关联对象、C++、或开启了sidetabled的引用计数
}
}
- 小对象类型使用的是taggedPointer,直接存储的值,因此不需要释放。
- isa.weakly_referenced weak类型标志位、has_assoc关联对象标志位、has_cxx_dtor C++标志位、has_sidetable_rc 引用计数有没有借用sidetable标志位。
- 释放分两种:如果2中情况都没有,free(this)释放当前对象,否则调用object_dispose进行释放。
因为weak、关联对象、C++、sidetable存储的结构不同,所以不同类型需要分开释放。
//objc-runtime-new.mm
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();//C++
bool assoc = obj->hasAssociatedObjects();//关联对象
// This order is important.
if (cxx) object_cxxDestruct(obj);// 调用C++析构函数
if (assoc) _object_remove_assocations(obj);// 移除所有的关联对象,并将其自身从Association Manager的map中移除
obj->clearDeallocating();// 清理相关的引用
}
return obj;
}
2. C++释放
//objc-class.mm
void object_cxxDestruct(id obj)
{
if (!obj) return;
if (obj->isTaggedPointer()) return;
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.
for ( ; cls; cls = cls->superclass) {
if (!cls->hasCxxDtor()) return;
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
if (dtor != (void(*)(id))_objc_msgForward_impcache) {
if (PrintCxxCtors) {
_objc_inform("CXX: calling C++ destructors for class %s",
cls->nameForLogging());
}
(*dtor)(obj);//调回.cxx_destruct的释放方法,释放必须交给用户
}
}
}
dtor = (void(*)(id))
lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);这一段与调用objc_msgsend方法类似。
void (*action)(id, SEL, NSString*) = (void (*)(id, SEL, NSString*))objc_msgSend;
action(self, @selector(SendImage:), fileName);
(*dtor)(obj);可以理解为调用了C++的析构方法,当前cls释放,superclass释放。
3. 关联对象释放
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());
}
RunTime源码阅读(二)关联对象这篇文章介绍过,一个对象对应一个AssociationsHashMap。因此释放掉AssociationsHashMap即可。
delete refs;删除ObjectAssociationMap对象,associations.erase(i);
ReleaseValue释放OBJC_ASSOCIATION_SETTER_RETAIN的value。
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);
}
}
4.弱引用与引用计数相关
obj->clearDeallocating();
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
void
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_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
看sidetable数据结构,同时管理弱引用与计数。所以释放可以放在一起。
weak_clear_no_lock是弱引用的释放。
4.1 弱引用释放
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);// 找到referent在weak_table中对应的weak_entry_t
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
// 找出weak引用referent的weak 指针地址数组以及数组长度
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];// 取出每个weak ptr的地址
if (referrer) {
if (*referrer == referent) {// 如果weak ptr确实weak引用了referent,则将weak ptr设置为nil,这也就是为什么weak 指针会自动设置为nil的原因
*referrer = nil;
}
else if (*referrer) {// 如果所存储的weak ptr没有weak 引用referent,这可能是由于runtime代码的逻辑错误引起的,报错
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
具体释放源码,参考RunTime源码阅读(一)之weak
由于weak的存储时分动态数组与静态数组,释放时需要区分。另外所谓的动态数组是判断当实际个数与总个数的大小进行比较,做扩容与收容操作。保证空间高效利用。
4.2 引用计数的释放
在开启isa指针优化后,大部分是用不到sidetable存储的。苹果分配了19位来存储extra_rc,也就是2^19-1= 524 287,大部分是够用的。
释放table.refcnts.erase(it);
总结:
- 整个释放过程还是比较复杂。建议还是跟着源码慢慢品读吧!
- 有任何问题欢迎留言评论