iOS是通過(guò)引用計(jì)數(shù)來(lái)管理對(duì)象的生命周期的,當(dāng)引用計(jì)數(shù)起為0的時(shí)候會(huì)調(diào)用delloc函數(shù)釋放該對(duì)象泻帮,在64bit中添寺,引用計(jì)數(shù)可以直接存儲(chǔ)在優(yōu)化過(guò)的isa指針中,也可能存儲(chǔ)在SideTable類中:
refcnts 是一個(gè)存放著對(duì)象引用計(jì)數(shù)的散列表,當(dāng)isa中存儲(chǔ)不下的時(shí)候會(huì)存儲(chǔ)在refcnts中捞蛋。執(zhí)行SideTable_release操作的時(shí)候引用計(jì)數(shù)會(huì)減一,引用計(jì)數(shù)器為0的時(shí)候會(huì)通過(guò)message_send執(zhí)行delloc函數(shù)柬姚,會(huì)在refcnts表中找到對(duì)象并釋放拟杉。
weak_table 一個(gè)全局的weak 引用哈希表:存放弱引用指針,會(huì)在弱引用的對(duì)象釋放的時(shí)候量承,通過(guò)該弱引用對(duì)象的地址找到弱引用指針搬设,釋放并至為nil穴店;
面試
- runtime 是怎么實(shí)現(xiàn)weak置nil的
- weak修飾的釋放則自動(dòng)被置為nil的實(shí)現(xiàn)原理
- ARC幫我們做了什么?
delloc方法釋放對(duì)象
當(dāng)一個(gè)對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì)執(zhí)行delloc函數(shù):
// Replaced by NSZombies
- (void)dealloc {
_objc_rootDealloc(self);
}
void
_objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
然后會(huì)通過(guò)對(duì)象的isa指針判斷對(duì)象是否有弱引用,C++析構(gòu)函數(shù)拿穴,關(guān)聯(lián)對(duì)象泣洞,如果沒(méi)有直接執(zhí)行free()釋放該對(duì)象,如果有執(zhí)行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在執(zhí)行free(obj)釋放對(duì)象前默色,會(huì)執(zhí)行objc_destructInstance(obj)釋放關(guān)聯(lián)對(duì)象球凰,弱引用,C++析構(gòu)函數(shù)
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
釋放弱引用腿宰,C++析構(gòu)函數(shù)呕诉,然后執(zhí)行clearDeallocating釋放關(guān)聯(lián)對(duì)象;
/***********************************************************************
* 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;
}
根據(jù)isa.nonpointer判斷isa類型是否是64位優(yōu)化后的指針吃度;
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表甩挫, 并把對(duì)象地址傳入,通過(guò)對(duì)象地址&table 算出散列表中的下標(biāo)椿每,然后清除引用的對(duì)象捶闸;
// 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();
}
通過(guò)這個(gè)方法獲取entry
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
然后執(zhí)行 weak_entry_remove(weak_table, entry) 從weak_table表中移除,然后執(zhí)行free()釋放該對(duì)象拖刃;
/**
* 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修飾的釋放則自動(dòng)被置為nil的實(shí)現(xiàn)原理?
1贪绘,創(chuàng)建person對(duì)象兑牡, 并被person2弱引用了,所以person2這個(gè)弱引用指針就會(huì)放在全局的weak_table表中税灌;
2均函,當(dāng)釋放要person對(duì)象的時(shí)候,通過(guò)runtime去weak_table表中找到person的弱引用person2菱涤,然后釋放person2這個(gè)弱引用指針苞也,并且設(shè)置person2=nil,然后再釋放person對(duì)象;
__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)
特別補(bǔ)充一點(diǎn)粘秆,如有需要會(huì)通過(guò)erase函數(shù)擦掉SideTable表中person的引用計(jì)數(shù):
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
- ARC幫我們做了什么?
ARC 其實(shí)就是LLVM + runtime如迟,LLVM會(huì)在編譯階段自動(dòng)幫我們添加[object release],然后會(huì)在runtime階段根據(jù)引用計(jì)數(shù)判斷攻走,當(dāng)引用計(jì)數(shù)為0的時(shí)候會(huì)通過(guò)isa指針的信息抹除對(duì)象的一些引用殷勘,這些都是runtime的功勞。