Block 簡介,block簡介
1、Blocks簡介
Block字面意思就是代碼塊 iOS4.0、Mac OS X 10.6開始Apple引入的特性
Block是Objective C語言中的對象 但是與NSObject有所區別 Block是特殊的Objective C對象
Block 對象提供了一個使用 C 語言和 C 派生語言(如 Objective-C 和 C++)來建立運算式作為一個特別的函數。在其他語言和環境中,一個block對象有時候被稱為“閉包(closure)”。在這裡,它們通常被口語化為”塊(blocks)”,除非在某些範圍它們容易和標準 C 運算式的塊代碼混淆。
對於閉包(closure),有很多定義,其中閉包就是能夠讀取其它函數內部變數的函數
“^”符號可以稱為caret ['kærət] 也叫脫字元 插入符
傳回值(^塊對象名稱)(參數清單類型) = ^(參數列表){塊對象中的代碼};
2、用處
1)簡單的回調過程,不用再實現並調用某個函數 (UIView動畫)
2)代碼簡潔,減少冗餘代碼
3)與GCD結合使用 爽爆了
使用:UIView動畫、presentViewController、ASI
3、聲明和建立Block
聲明Block引用 無參無返回 無參有返回 有參無返回 有參有返回
定義Block
使用Block
typedef聲明 簡稱typedef 為現有類型建立一個新的名字,或稱為類型別名,在結構體定義,還有一些數組等地方都會用到
傳回值或參數為Block的
snippet 程式碼片段
4、Block對變數存取管理
1)局部變數
局部變數,在Block中唯讀。Block定義時copy變數的值,在Block中作為常量使用,所以即使變數的值在Block外改變,也不影響它在Block中的值
2)__Block修飾的變數
如果要在block內修改block外聲明的局部變數,那麼一定要對該變數加__block標記
3)Static修飾符的或全域變數
因為全域變數或靜態變數在記憶體中的地址是固定的,Block在讀取該變數值的時候是直接從其所在記憶體讀出,擷取到的是最新值,而不是在定義時copy的常量.
Block變數,被__Block修飾的變數稱作Block變數。 基本類型的Block變數等效於全域變數或靜態變數 但對象的block變數不會
5、Block的記憶體管理
非ARC下
Block是預設建立在棧上, 所以如果離開方法範圍, Block就會被丟棄
Block的copy、retain、release操作 不同於NSObject的copy、retain、release操作:
只要實現一個對周圍變數沒有引用的Block,就會顯示為是NSGlobalBlock
如果其中加入了對局部變數的引用,就是NSStackBlock
如果你對一個NSStackBlock對象使用了Block_copy()或者發送了copy訊息,就會得到NSMallocBlock
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操作,不方便管理。
Block的使用:UIView動畫、presentViewController、ASI
6、Block對objc對象的記憶體管理
staticObj、globalObj、instanceObj、localObj、blockObj多種類型obj對象
主要是block被copy時其塊中用到的變數的引用計數
1)非ARC
globalObj和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];
};
7、循環參考retain cycle
循環參考指兩個對象相互強引用了對方,即retain了對方,從而導致誰也釋放不了誰的記憶體泄露問題。如聲明一個delegate時一般用assign而不能用retain或strong,因為你一旦那麼做了,很大可能引起循環參考
釋放second 在fist delloc中釋放 fist的delloc什麼時候執行呢 ?
fist引用計數為0時執行 ,然而現在即便是將fist從window.rootViewController上卸載下來 即釋放一次 卻發現second還保留著first的一次引用 到頭來還是要釋放second 形成了delegate版本的retain cycle 即循環參考
釋放_pBlock 在viewController delloc中釋放 delloc什麼時候執行呢?
viewController引用計數為0時執行 ,然而現在即便是將viewController從window.rootViewController上卸載下來 即釋放一次 卻發現_pBlock還保留著viewController的一次引用 到頭來還是要釋放_pBlock 形成了block版本的retain cycle 即循環參考
Block的記憶體管理
ARC下
在ARC下, 以下幾種情況, Block會自動被從棧複製到堆:
1.被執行copy方法
2.作為方法傳回值
3.將Block賦值給附有__strong修飾符的id類型的類或者Blcok類型成員變數時
4.在方法名中含有usingBlock的Cocoa架構方法或者GDC的API中傳遞的時候.
Block中的對象的記憶體管理
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、方法區