標籤:
首先交代一下retain cycle ,和 產生retain cycle後我們應該怎麼處理。
1.retain cycle在block中是極易產生,block就是一段可以靈活使用的代碼,你可以把它當做變數傳遞,賦值,甚至可以把它聲明到函數體中。更加靈活的是它可以引用它的承載著(即就是block的運行環境),但是這樣子就更容易產生retain cycle了 。就是簡單說類比 子控制項擁有父控制項的引用,而父控制項也擁有子控制項的引用。這樣相互引用。就不能形成父控制項釋放也能將子控制項釋放。這就造成一個記憶體泄露。
2.及時打破這種retain cycle ARC :__strong , __weak ,__unsafe_unretained.存在有三個修飾字元,__strong 這要有調用,引用計數就要加1(retain) __unsafe_unretained 賦值給這個變數不會被retain,就是有這個字元修飾的對象,不能保證對象的可靠性。可能已經釋放了。留下的只是一個野指標而已。__weak是ios5以後出現的類比與__unsafe_unretained,不同的地方就是。它所持有對象被釋放後會自動被賦值為nil。更加安全了。
正如下面的代碼就是一個典型的retain cycle
People *p=[People new];
p.dosth = ^(){
[p dosth];
[p release]; //因為dosth和p是相互持有關係,所以調用release也不能釋放掉
};
1.我們可以這樣子處理(自己心裡應該清楚,我們不會再次回來調用這個方法了)
People *p=[People new];
p.dosth = ^{
[p dosth];
p.dosth=nil;
[p release]; //因為dosth和p是相互持有關係,所以調用release也不能釋放掉
};
2.在ARC情況下
在ARC下, 以下幾種情況, Block會自動被從棧複製到堆:
1.被執行copy方法
2.作為方法傳回值
3.將Block賦值給附有__strong修飾符的id類型的類或者Blcok類型成員變數時
4.在方法名中含有usingBlock的Cocoa架構方法或者GDC的API中傳遞的時候.
利用
__block 將變數的地址考進了棧空間,這樣子我們就可以在Block中方便無比的調用最新的變數值了。因為block的空間也是在棧空間的.block記憶體是在棧上,(不需要程式員管理),當你這個範圍{方法}結束的時候,block被釋放了.
__weak 被__weak修飾的變數,是不會被block retain的,但是也會同時引出提前釋放變數的後果
3.在非ARC情況下
只要實現一個對周圍變數沒有引用的Block,就會顯示為是NSGlobalBlock
如果其中加入了對局部變數的引用,就是NSStackBlock
如果你對一個NSStackBlock對象使用了Block_copy()或者發送了copy訊息,就會得到NSMallocBlock (0)
1)NSGlobalBlock:retain、copy、release操作都無效;
2)NSStackBlock:retain、release操作無效,必須注意的是,NSStackBlock在函數返回後,Block記憶體將被回收。即使retain也沒用。容易犯的錯誤是[mutableAarry addObject:stackBlock],(補:在ARC中不用擔心此問題,因為ARC中會預設將執行個體化的Block拷貝到堆上)在函數出棧後,從mutableAarry中取到的stackBlock已經被回收,變成了野指標。正確的做法是先將[stackBlock copy]到堆上,然後加入數組:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支援copy,copy之後產生新的NSMallocBlock類型對象。
3)NSMallocBlock支援retain、release,雖然retainCount始終是1,但記憶體管理器中仍然會增加、減少計數。copy之後不會產生新的對象,只是增加了一次引用,類似retain;
4)Block_copy與copy等效,Block_release與release等效;
5)對Block不管是retain、copy、release都不會改變引用計數retainCount,retainCount始終是1;
6)盡量不要對Block使用retain操作,不方便管理。
4、Block對objc對象的記憶體管理
staticObj、instanceObj、localObj、blockObj多種類型obj對象
主要是block被copy時其塊中用到的變數的引用計數
1)非ARC
staticObj在記憶體中的位置是確定的,所以Block copy時引用計數不會改變。
instanceObj在Block copy時並沒有直接讓instanceObj對象本身引用計數加1,但卻讓self引用計數加1。所以在Block中可以直接讀寫instanceObj變數。
localObj在Block copy時,系統自動增加其引用計數。
blockObj在Block copy時引用計數也不會改變。
使用__block避免循環參考
__block 類 *對象 = self
void(^block)(void)= ^{
[blockSelf doSomething];
};
ARC下
只有在使用local變數時,block會複製指標,且強引用指標指向的對象一次。其它如全域變數、static變數、block變數等,block不會拷貝指標,只會強引用指標指向的對象一次。
block的循環參考,因為block在拷貝到堆上的時候,會retain其引用的外部變數,那麼如果block中如果引用了它的宿主對象,那很有可能引起循環參考。如:
self.myblock = ^{
[self doSomething];
};
使用__weak避免循環參考
Tips:
記憶體主要分為
1.棧 - 由編譯器自動分配釋放 裡面的變數通常是局部變數 函數參數等
2.堆 - 一般由程式員分配釋放,若程式員不釋放,程式結束時可能由OS回收 alloc
3.全域區(靜態區 static),全域變數和靜態變數的儲存是放在一塊的,初始化的全域變數和靜態變數在一塊地區,未初始化的全域變數和未初始化的靜態變數在相鄰的另一塊地區。- 程式結束釋放 static
People *p; People *p2 = nil;
4.另外還有一個專門放常量的地方。- 程式結束釋放 NSString *lastName = @“xue”;
lastName = @“dkjs”;
5、方法區
對 block 記憶體管理的一些看法