__weak修飾符相對於__strong修飾,是爲了解決循環引用的。
@interface Test: NSObject
{
id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implement Test
- (id)init {
self = [super init];
return self;
}
- (void)setObject:(id __storng)obj {
obj_ = obj;
}
@end
{
id test0 = [Test new]; /* 對象A */
/*
* test0持有Test對象A的強引用
* /
id test1 = [Test new]; /* 對象B */
/*
* test1持有Test對象B的強引用
* /
[test0 setObject:test1];
/*
* Test對象A的obj_成員變量持有Test對象B的強引用
* 此時,持有Test對象B的強引用的變量爲Test對象A的obj_和test1
*/
[test1 setObject:test0];
/*
* Test對象B的obj_成員變量持有Test對象A的強引用
* 此時,持有Test對象A的強引用的變量爲Test對象B的obj_和test0
*/
} /*
* 因爲test0變量超出其作用域,強引用失效,自動釋放Test對象A
* 因爲test1變量超出其作用域,強引用失效,自動釋放Test對象B
* 持有Test對象A的強引用的變量爲Test對象B的obj_
* 持有Test對象B的強引用的變量爲Test對象A的obj_
* 發生內存泄漏
*/
上面的例子就是一個明顯的引用循環。可以用weak來解決
@interface Test: NSObject
{
id __strong obj_;
} /*
* 相互不擁有
* /
weak還有以下2點特性
- 1.若附有__weak修飾符的變量所引用的對象被廢棄,則將nil賦值給該變量
- 2.使用__weak修飾符的變量,即是註冊到autoreleasepool中的對象
1
{
id __weak obj1 = obj;
}
/*上述語句在編譯器中的模擬源碼*/
id obj1;
objc_initWeak(&obj1, obj)
objc_destoryWeak(&obj1)
objc_initWeak先初始化變量,然後再作用域結束時調用objc_destoryWeak釋放該變量。
而objc_initWeak(&objc1,obj)的源碼可以如下表示
obj1 = 0;
objc_storeWeak(&obj1,obj);
objc_storeWeak函數把第二參數的賦值對象的地址作爲鍵值,將第一參數的附有 __weak修飾符的變量的地址註冊到weak表中,如果第二參數爲0,則把變量的地址從weak表中刪除。刪除操作執行爲以下內容:
- 從weak表中獲取廢棄對象的地址爲鍵值的記錄
- 將包含在記錄中所有附有__weak修飾符的變量的地址,賦值爲nil
- 從weak表中刪除該紀錄
- 從引用計數表中刪除廢棄對象的地址爲鍵值的記錄
由此可知,當大量使用附有__weak修飾符的變量,則會消耗很多CPU資源,因爲會進行如上操作。所以比較好的策略是隻在避免循環引用時使用__weak修飾符。
2.
id __weak obj1 = obj;
NSLog(@"%@", obj1);
/*編譯器的模擬代碼*/
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destoryWeak(&obj1);
從上面的代碼可知,objc_loadWeakRetained函數取出__weak修飾符變量所引用的對象並retain,objc_autorelease函數將對象註冊到autoreleasepool中。
因此每次使用附有__weak修飾符的對象都會在autoreleasepool中創建對象,所以大量使用時,建議先暫時賦值給 __strong修飾符的變量後再使用。
id __weak o = obj;
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
/*
* obj對象會在autoreleasepool中註冊5次
* /
id __weak o = obj;
id tmp = o;
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
/*
* obj對象在autoreleasepool中僅註冊1次
* /