1、block簡介
block字面意思就是代碼塊
iOS4.0 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結合使用 爽爆了
3、block的聲明、使用
聲明block
block實現
調用block(代碼的執行順序)
typedef聲明 簡稱typedef 為現有類型創建一個新的名字,或稱為類型別名,在結構體定義,還有一些數組等地方都會用到
block作為屬性 使用copy
方法參數為block
(簡單了解)返回值為block 返回值block的類型要typedef
4、系統方法使用block 以及 系統方法內部的實現、實現兩個自定義類
動畫、present
blockButton
block和代理
snippet 代碼片段
5、block中變量存取管理
1)Static修飾符的或全局變量
因為全局變量或靜態變量在內存中的地址是固定的,block在讀取該變量值的時候是直接從其所在內存讀出,獲取到的是最新值,而不是在定義代碼塊時copy的常量.
2)局部變量
局部變量 在block中只讀, block定義時copy變量的值,在block中作為常量使用,所以即使變量的值在block外改變,也不影響它在block中的值
3)__block修飾的變量
如果要在block內修改block外聲明的局部變量,那么一定要對該變量加__block標記
block變量,被__block修飾的變量稱作block變量
6、block自身的內存管理
block是默認建立在棧上, 所以如果離開方法作用域, block就會被丟棄
非ARC下
只要實現一個對周圍變量沒有引用的block,就會顯示為是NSGlobalblock
如果其中加入了對周圍變量的引用,就是NSStackblock
如果你對一個NSStackblock對象使用了block_copy()或者發送了copy消息,就會得到NSMallocblock
1)NSGlobalblock:retain、copy、release操作都無效;
2)NSStackblock:retain、release操作無效,必須注意的是,NSStackblock在函數返回后,block內存將被回收。即使retain也沒用。容易犯的錯誤是[mutableAarry addObject:stackblock],在函數出棧后,從mutableAarry中取到的stackblock已經被回收,變成了野指針。正確的做法是先將[stackblock copy]到堆上,然后加入數組:[mutableAarry addObject:[[stackblock copy]]。支持copy,copy之后生成新的NSMallocblock類型對象。(補:在ARC中不用擔心此問題,因為ARC中會默認將實例化的block拷貝到堆上)
3)NSMallocblock支持retain、release,雖然打印的retainCount始終是1,但內存管理器中仍然會增加、減少計數。copy之后不會生成新的對象,只是增加了一次引用,類似retain;
block的copy、retain、release操作不同于NSObject的copy、retain、release操作:
4)block_copy與copy等效,block_release與release等效;
5)對block不管是retain、copy、release都不會改變引用計數retainCount,retainCount始終是1;
6)盡量不要對block使用retain操作,不方便管理。
7、block對block中使用的obj對象的內存管理
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];
};
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下
在ARC下, 以下幾種情況, block會自動被從棧復制到堆:
1.被執行copy方法
2.作為方法返回值
3.將block賦值給附有__strong修飾符的id類型的類或者Blcok類型成員變量時
4.在方法名中含有usingblock的Cocoa框架方法或者GCD的API中傳遞的時候.
block中的對象的內存管理
ARC下
只有在使用local變量時,block會復制指針,且強引用指針指向的對象一次。其它如全局變量、static變量、block變量等,block不會拷貝指針,只會強引用指針指向的對象一次。
block的循環引用,因為block在拷貝到堆上的時候,會retain其引用的外部變量,那么如果block中如果引用了它的宿主對象,那很有可能引起循環引用。如:self.myblock = ^{[selfdoSomething];};
使用__weak避免循環引用
__weak typeof(ViewController *) weakSelf = self
self.myblock = ^{[weakSelfdoSomething];}
Tips:
內存主要分為
1、棧區(stack)— 由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。
2、堆區(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收。
3、全局區(靜態區)(static)—,全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域,未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。程序結束后由系統釋放。
4、文字常量區 —常量字符串就是放在這里的。程序結束后由系統釋放。
5、程序代碼區—存放函數體的二進制代碼。