__strong##
strong 修飾符是oc對象的默認修飾符,一般我們在生成一個對象的時候就默認添加了strong修飾,例如:
id arr = [[NSArray alloc] init] 等同于 id __strong arr = [[NSArray alloc] init]
strong修飾符表示對對象的強引用,那么什么是強引用,什么是弱引用,其實這些都是編譯器層面的特性,與實際的內(nèi)層方面沒有太大的關(guān)系,編譯器在處理使用strong修飾符修飾的變量和在處理使用weak修飾符修飾的變量的機制是不一樣的。持有強引用的變量在超出其作用域時會被釋放掉,強引用失效,那么其引用的對象也會隨之被廢棄掉。如下面這兩段代碼也是相等的關(guān)系:
{
//自己生成的對象自己持有
id __strong arr = [[NSArray alloc] init];
NSLog(@"%@",arr);//輸出對象內(nèi)存地址
}//變量arr超出其作用域,強引用失效,所以其引用的對象[[NSArray alloc] init] 被廢棄
{
id arr = [[NSArray alloc] init];
[arr release];//調(diào)用release方法直接廢棄對象
NSLog(@"%@",arr); //輸出nil
}
當然如果用strong去修飾的是非自己生成并持有的對象,只要超出變量的作用域其結(jié)果就跟上面的一樣。
在strong修飾符修飾的對象之間可以相互進行賦值操作,賦值之后新變量也是strong類型,原變量被廢棄。原變量所引用的對象也隨之被廢棄,原變量與新變量指向同一塊內(nèi)存空間例如:
{
id __strong obj1 = [[NSObject alloc] init];
id __strong obj2 = [[NSObject alloc] init];
id __strong obj3 = nil;
obj1 = obj2;
NSLog(@"%@ -- %@",obj1,obj2);//輸出 <NSObject: 0x600000012b70> -- <NSObject: 0x600000012b70>
obj3 = obj1;
NSLog(@"%@ -- %@",obj3,obj1); //輸出 <NSObject: 0x600000012b70> -- <NSObject: 0x600000012b70>
}
通過上面的例子可以看到,__strong修飾的變量不僅僅只在變量作用域中,在賦值上一樣的可以管理其對象的所有者。
正如蘋果說的。通過strong修飾符,人們就可以不用去輸入retain和release代碼也可以正確的管理內(nèi)存,這就是使用引用計數(shù)來管理內(nèi)存的思考方式:
1,自己生成的對象自己所持有,
2,非自己生成的對象自己也能持有,
3,不再需要自己持有的對象時釋放,
4,非自己持有的對象無法釋放。
只需要通過對帶strong修飾符的變量賦值就可以達到 前兩項自己生成的對象自己持有和非自己生成的對象自己也能持有的效果, 通過廢棄帶strong修飾符的變量(變量作用域結(jié)束或是成員變量所屬對象廢棄)或者對變量賦值都可以做到 不再需要自己持有的對象時釋放。 最后一項 非自己持有的對象無法釋放 因為 不必再次鍵入release,所以原本就不會執(zhí)行,這些都滿足引用計數(shù)式內(nèi)存管理的思考方式。
__weak##
使用strong 修飾變量的時候我們會發(fā)現(xiàn)一個問題,比如下面這段代碼:
//測試代碼
@interface Test : NSObject
@property(nonatomic, strong)id obj;
@end
@implementation Test
-(id)init{
self = [super init];
return self;
}
-(void)setObj:(id)obj{
_obj = obj;
}
@end
{
Test *test0 = [[Test alloc] init]; //A對象
Test *test1 = [[Test alloc] init];//B對象
//此時B對象的持有者為test1 和 A對象的成員變量obj,B對象被兩條強指針指向
[test0 setObj:test1];
//此時A對象的持有者為test0 和 B對象的成員變量obj,A對象被兩條強指針指向
[test1 setObj:test0];
}
/*
test0 超出其作用域 ,所以其強引用失效,自動釋放對象A;
test1 超出其作用域 ,所以其強引用失效,自定釋放對象B;
此時持有象A的強引用的變量為B對象的成員變量obj;
此時持有對象B的強引用的變量為A對象的成員變量obj;
因為都還被一條強指針指向,所以內(nèi)存泄漏!
*/
從上面的代碼可以看出,對象B本該在超過作用域后釋放的,但是A對象的成員變量obj一直強引用著它,對象B的引用計數(shù)一直不能減為0,所以導致對象B一直無法釋放,對象A也是一樣,也因為被對象B的成員變量obj強引用所以也一直無法釋放,這兩個對象就像一只咬住自己尾巴的蛇進入了死鎖狀態(tài)導致這兩塊內(nèi)存無法被回收,從而發(fā)生內(nèi)存泄漏。大家可以好好體會下。
那么要解決上面這個問題,該怎么辦,顯然蘋果已經(jīng)考慮到了,那就是引入弱引用__weak。它跟strong的區(qū)別就是它不能持有對象的實例,比如:
id __weak obj = [[NSObject alloc] init];//對象A
已寫完這行代碼編譯器就會發(fā)生警告:
在沒有任何賦值的時候。對象A一被創(chuàng)建就會馬上被釋放。寫成下面這樣就不會了:
{
id __weak obj = [[NSObject alloc] init];
id __strong obj1 = obj;
}
因為使用weak修飾的變量不能持有對象的實例,所以在MRC下也就意味著引用計數(shù)不會加1,所以內(nèi)存可以被正確回收。
下面再來看下__weak的另一種好處:
id __weak obj = @;
{
Test *obj1 = [[Test alloc] init];// 對象A
obj1 = obj;
NSLog(@"obj1 = %@",obj1);//輸出 obj1 = <Test: 0x600000018be0>
}
NSLog(@"obj = %@",obj);輸出 0bj = (null)
從以上代碼可以看出在持有某對象的弱引用時,弱該對象被廢棄了,那么此弱引用將自動失效且弱引用修飾的變量自動置為nil。
像這樣,使用__weak修飾符可以很好地避免循環(huán)引用的出現(xiàn),這也是我們在block里面使用weakSelf 而不使用self 的原因,因為block會捕獲self,而self又持有block,導致兩個互相指向從而發(fā)生內(nèi)存泄漏,代理也是一樣也要用weak修飾來避免循環(huán)引用。
那么如果項目中已經(jīng)發(fā)生了循環(huán)引用的話我們應該如何來排查呢,請關(guān)注我的下一篇文章,我會教你如何查出項目中哪些地方發(fā)生了循環(huán)引用。