iOS是通过引用计数来管理对象的生命周期的,当引用计数起为0的时候会调用delloc函数释放该对象,在64bit中,引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable类中:
refcnts 是一个存放着对象引用计数的散列表,当isa中存储不下的时候会存储在refcnts中。执行SideTable_release操作的时候引用计数会减一,引用计数器为0的时候会通过message_send执行delloc函数,会在refcnts表中找到对象并释放。
weak_table 一个全局的weak 引用哈希表:存放弱引用指针,会在弱引用的对象释放的时候,通过该弱引用对象的地址找到弱引用指针,释放并至为nil;
面试
- runtime 是怎么实现weak置nil的
- weak修饰的释放则自动被置为nil的实现原理
- ARC帮我们做了什么?
delloc方法释放对象
当一个对象的引用计数为0的时候会执行delloc函数:
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
然后会通过对象的isa指针判断对象是否有弱引用,C++析构函数,关联对象,如果没有直接执行free()释放该对象,如果有执行object_dispose();
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
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);
}
}
object_dispose在执行free(obj)释放对象前,会执行objc_destructInstance(obj)释放关联对象,弱引用,C++析构函数
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
释放弱引用,C++析构函数,然后执行clearDeallocating释放关联对象;
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
根据isa.nonpointer判断isa类型是否是64位优化后的指针;
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());
}
weak_clear_no_lock(&table.weak_table, (id)this);获取全局weak_table表, 并把对象地址传入,通过对象地址&table 算出散列表中的下标,然后清除引用的对象;
// Slow path of clearDeallocating()
// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
通过这个方法获取entry
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
然后执行 weak_entry_remove(weak_table, entry) 从weak_table表中移除,然后执行free()释放该对象;
/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
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);
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;
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];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_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);
}
/**
* Return the weak reference table entry for the given referent.
* If there is no entry for referent, return NULL.
* Performs a lookup.
*
* @param weak_table
* @param referent The object. Must not be nil.
*
* @return The table of weak referrers to this object.
*/
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
while (weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
return &weak_table->weak_entries[index];
}
面试
- weak修饰的释放则自动被置为nil的实现原理?
1,创建person对象, 并被person2弱引用了,所以person2这个弱引用指针就会放在全局的weak_table表中;
2,当释放要person对象的时候,通过runtime去weak_table表中找到person的弱引用person2,然后释放person2这个弱引用指针,并且设置person2=nil,然后再释放person对象;
__strong Person *person1;
__weak Person *person2;
__unsafe_unretained Person *person3;
NSLog(@"111");
{
Person *person = [[Person alloc] init];
person2 = person;
}
NSLog(@"222 - %@", person2);
111
[Person dealloc]
222 - (null)
特别补充一点,如有需要会通过erase函数擦掉SideTable表中person的引用计数:
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
- ARC帮我们做了什么?
ARC 其实就是LLVM + runtime,LLVM会在编译阶段自动帮我们添加[object release],然后会在runtime阶段根据引用计数判断,当引用计数为0的时候会通过isa指针的信息抹除对象的一些引用,这些都是runtime的功劳。