iOS循環引用

所謂的循環引用是:當A對象裏面強引用了B對象,B對象又強引用了A對象,這樣兩者的RetainCount值一直無法爲0,於是內存時鐘無法釋放,導致內存泄漏。所謂的內存泄漏就是本應該釋放的對象,在其生命週期結束之後依舊存在。這種循環還能存在於3,4……個對象之間,只要相互形成環,就會導致Retain Cicle的問題。
還有一種自身引用自身的,當一個對象內部的一個obj,強引用的自身,也會導致循環引用的問題出現。常見的就是Block裏面引用的問題


平常我們常見的循環引用有block循環引用和對象之間相互強引用兩種情況;
1、block循環引用問題:例如

#import "Person.h"
typedef void(^PersonBlock)(NSString *name);
@interface Person()
@property (nonatomic,strong)NSArray *propertyS;
@property (nonatomic,strong)PersonBlock block;
@end
@implementation Person
- (id)init
{
    if (self = [superinit]) {
        self.propertyS =@[@"女",@"23"];
        self.block = ^(NSString *name){
            NSLog(@"arr:%@",self.propertyS);
        };
    }
    return self;
}
@end

其中在block中調用propertyS會有警告出現,這是因爲在block中調用了self所擁有的東西,引起了循環引用,那麼這個循環引用具體怎樣引起的呢?
(1)block中引用了self,self被block retain,propertyS又retain了該block的一個拷貝
(2)propertyS是在self中定義賦值的,因此是被self retain的
因此形成了循環引用,不會調用dealloc
解決方法是利用__weak或者__unsafe__unretained去修飾self(在ARC模式下),在MRC模式下用__block修飾

2、兩個對象之間相互強引用:

@interface Person : NSObject
{
    Person  *person1;
    Person  *person2;
    NSArray *array;
}
@property (readwrite,assign)Person *person1;
@property (readonly)Person *person2;

@end

person擁有兩個Person對象,每個對象又都擁有一個NSArray對象。屬性person1和person2都指向了彼此的Person對象。那麼兩個對象間形成了強引用,ARC將不會銷燬這個對象。
爲了阻止這種事的發生,聲明其中一個屬性爲弱引用即可(__weak Person *person2)。這樣,當person2指向空並且Person對象超出範圍時,ARC可以安全的給這個對象發送一個release消息。

__weak指示符既可以聲明弱引用,也可以聲明空引用。__unsafe_unretained聲明弱引用,不是空引用。如果要用後者,那麼要自己處理空引用,否則會內存泄漏


以下內容轉載自:http://www.jianshu.com/p/701da54bd78c
ARC環境下,所有權修飾符:
1、__strong
2、__weak
3、__unsafe_unretained
4、__autoreleasing
一般我們如果不寫,默認的修飾符是__strong

1、__strong的實現原理
(1) 首先我們先來看看生成的對象持有自己的情況,利用alloc/new/copy/mutableCopy生成對象。當我們聲明瞭一個__strong對象
id __strong obj = [[NSObject alloc] init];
LLVM編譯器會把上述代碼轉換成下面的樣子:

 `id __attribute__((objc_ownership(strong))) obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));`

相應的會調用

id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj,selector(init));
objc_release(obj);

在ARC有效的時候就會自動插入release代碼,在作用域結束的時候自動釋放。
(2)對象不持有自己
生成對象的時候不用alloc/new/copy/mutableCopy等方法。例如:
id __strong obj = [NSMutableArray array];
LLVM編譯器會把上述代碼轉換成下面的樣子:

id __attribute__((objc_ownership(strong))) array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));

相應的會調用

id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);

與之前對象會持有自己的情況不同,這裏多了一個objc_retainAutoreleasedReturnValue函數。
objc_retainAutoreleasedReturnValue函數是用於自己持有(retain)對象的函數,它持有的對象應爲返回註冊在autoreleasepool中對象的方法或者是函數的返回值。

在ARC中原本對象生成之後是要註冊到autoreleasepool中,但是調用了objc_autoreleasedReturnValue 之後,緊接着調用了 objc_retainAutoreleasedReturnValue,objc_autoreleasedReturnValue函數會去檢查該函數方法或者函數調用方的執行命令列表,如果裏面有objc_retainAutoreleasedReturnValue()方法,那麼該對象就直接返回給方法或者函數的調用方。達到了即使對象不註冊到autoreleasepool中,也可以返回拿到相應的對象。

2.__weak的實現原理
聲明一個__weak對象
id __weak obj = strongObj;
假設這裏的strongObj是一個已經聲明好了的對象
LLVM轉換成對應的代碼
id attribute((objc_ownership(none))) obj1 = strongObj;
相應的會調用

id obj ;
objc_initWeak(&obj,strongObj);
objc_destoryWeak(&obj);

objc_initWeak的實現其實是這樣的

id objc_initWeak(id *object, id value) {   
    *object = nil; 
    return objc_storeWeak(object, value);
}

會把傳入的object變成0或者nil,然後執行objc_storeWeak函數。
objc_destoryWeak函數的實現

void objc_destroyWeak(id *object) { 
    objc_storeWeak(object, nil);
}

objc_initWeak和objc_destroyWeak函數都會去調用objc_storeWeak函數,唯一不同的是調用的入參不同,一個是value,一個是nil。
objc_storeWeak函數就把第一個入參的變量地址註冊到weak表中,然後根據第二個入參來決定是否移除。如果第二個參數爲0,那麼就把__weak變量從weak表中刪除記錄,並從引用計數表中刪除對應的鍵值記錄。

所以如果__weak引用的原對象如果被釋放了,那麼對應的__weak對象就會被指爲nil。原來就是通過objc_storeWeak函數這些函數來實現的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章