IOS 程式碼塊之block的宣告、建立、傳參的基本使用
Block 是iOS在4.0之後新增的程式語法,在iOS SDK 4.0之後,block應用幾乎無處不在。
在其他語言中也有類似的概念稱做閉包(closure),比如object C的好兄弟Swift 中閉包(swift 閉包詳解)的使用跟 OC的block一樣重要。總的來說:
Block是C語言的
Block是一個數據型別
Block 是一個提前準備好的程式碼,在需要的時候執行
1. block作用:
Block用來封裝一段程式碼,可以在任何時候執行;
- Block可以作為函式引數或者函式的返回值,而其本身又可以帶輸入引數或返回值。
- 蘋果官方建議儘量多用block。在多執行緒、非同步任務 、集合遍歷、集合排序、動畫轉場用的很多
在新的iOS API中block被大量用來取代傳統的delegate和callback,而新的API會大量使用block主要是基於以下兩個原因:
A. 可以直接在block程式碼塊中寫等會要接著執行的程式碼,直接把block變成函式的引數傳入函式中,這是新API最常使用block的地方。
B. 可以存取區域性變數,在傳統的callback操作時,若想要存取區域性變數得將變數封裝成結構體才能使用,而block則是可以很方便地直接存取區域性變數。
2. Block的定義:
定義時,把block當成資料型別
特點:
1. 型別比函式定義多了一個 ^
2. 設定數值,有一個 ^,內容是 {} 括起的一段程式碼
(1)基本定義方式
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
(2) block 指標
Block Pointer是這樣定義的:
回傳值 (^名字) (引數列);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
(3) 用typedef先宣告型別,再定義變數進行賦值
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
(4) block 訪問外部變數
但是block使用有個特點,Block可以訪問區域性變數,但是不能修改:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
如果要修改就要加關鍵字 __block (下面詳細說明):
__block int sum =10;
(5) block 與函式指標
下面比較下函式指標與block異同:
-
定義函式指標
int (*myFn)();
呼叫函式指標(*myFn)(10, 20);
-
定義Block
int (^MyBlocks)(int,int);
呼叫BlocksMyBlocks(10, 20);
3. block訪問外部變數
block 訪問外部變數有幾個特點必須知道:
- block內部可以訪問外部變數;
- 預設情況下block內部不能
修改
外面的區域性變數; - 給區域性變數加上關鍵字
_block
,這個區域性變數就可以在block內部修改;
block中可以訪問外部變數。但是
不能修改它
,否則編譯錯誤
。但是可以改變全域性變數、靜態變數(static)、全域性靜態變數。
上面的特點是有原因滴:
A.
為何不讓修改變數
:這個是編譯器決定的。理論上當然可以修改變量了,只不過block捕獲的是外部變數的副本,名字一樣。為了不給開發者迷惑,乾脆不讓賦值。道理有點像:函式引數,要用指標,不然傳遞的是副本(大家想起那個經典的兩個數調換值的問題了吧)。B.
可以修改靜態變數的值
。靜態變數屬於類的,不是某一個變數。所以block內部不用呼叫cself指標。所以block可以呼叫。
(1) __block儲存型別
通過__block儲存型別修飾符, 變數在block中可被修改。__block儲存跟register、auto和static儲存型別相似(但是之間互斥),用於區域性變數。__block變數儲存在堆區
,因此,這個block使用的外部變數,將會在棧結束被留下來。
從優化角度考慮,block儲存在棧上,如果block被拷貝(通過Block_copy或者copy),變數被拷貝到堆
。因此__block變數的地址就會改變。
__block變數還有兩個限制,他們不能是可變陣列(NSMutableArray),不能是結構體(structure)。
__block 變數的內部實現要複雜許多,__block 變數其實是一個結構體物件,拷貝的是指向該結構體物件的指標
(2) block訪問外部變數
上面已經說過,預設block 訪問的外部變數是隻讀屬性的,若要對外部變數進行讀寫,需要在定義外部變數時加一個 __block, 示例如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
下面的例子就有點難度了,讓我們看下block對指標變數的訪問
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
上面的例子搞定了,來讓我們看下各種型別的變數與block之間的互動:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
(3) block 引用成員變數
OC物件,不同於基本型別
,Block會引起物件的引用計數變化。若我們在block中引用到oc的物件,則物件的引用計數器會加1, 不過在物件前 加__block修飾,則參考計數不變。
- 若直接存取例項變數(instance variable),self的參考計數將被加1。
- 若透過變數存取例項變數的值,則變數的參考計數將被加1。
- 在物件前加 __block 則參考計數不會自動加1。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
上面只是簡單演示下block引用成員變數,下面我們研究下block引用成員變數時出現的一個經典問題:迴圈引用
。
在block內部使用成員變數,如下: