學習OC有接觸到一個新詞Block(個人感覺又是一個牛氣沖天的詞),但不是新的概念,不是新的東西。學過Javascript的小夥伴對閉包應該不陌生吧~學過PHP的應該也不陌生,在PHP5.3版本以後也支援閉包, 也就是OC中所提到的Block。 到底什麼是閉包或者block呢?用大白話說就是匿名函式,也就是在函式中可以包含這函式。就是在函式中可以定義匿名函式然後在函式中呼叫。學習OC中的block之前也小擔心一下,Block在OC中屬於高階的部分,心裡有又有個疑問:學起來難不難?看過Block的部分,感覺Block挺好理解的,用起來也挺順手的,Block沒我想象中的那麼難理解。
廢話少說,學習一門新的程式語言是少不了程式碼量的支援的,所以程式碼是少不了的。下面就通程式碼來認識一下OC中的block的使用。
Block基礎部分
1.Block的宣告
Block的定義和函式的宣告差不多,就是把函式名改成(^blockName)即可。下面是block宣告的程式碼。
有返回值的
1 |
int (^sumBlock) (int, int); |
無返回值的
1 |
void (^myBlock)(int, int); |
2.給block塊賦值
給宣告好的block,賦值。block的值就是個函式體,給block塊賦值有兩種方式,一個在宣告的時候賦值,一個是先宣告在賦值。
先宣告再賦值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//程式碼塊的宣告 void (^myBlock)(int, int); //給程式碼塊賦值 myBlock = ^(int a, int b) { //test ++; //報錯 NSLog(@"main_test = %d", test); //blockVar++不報錯; blockVar ++; NSLog(@"blockVar = %d", blockVar); int sum = a + b; NSLog(@"a + b = %d", sum); }; |
在宣告的時候賦值
1 2 3 4 5 |
int (^sumBlock) (int, int) = ^(int a, int b) { int sum = a + b; return sum; }; |
3.呼叫block
block的使用和普通函式的使用相同,呼叫方法如下:
1 2 |
//呼叫程式碼塊並接收返回值 int sum = sumBlock(20, 30); |
4.把block當做引數傳入函式
1 2 3 4 5 6 |
//把程式碼塊作為函式引數 void blockFunction(int (^myBlock)(int, int)) { int sum = myBlock(10,20); NSLog(@"fun_sum = %d", sum); } |
5.在程式碼塊中使用區域性變數和全域性變數
在block中可以和對全域性變數進行訪問和修改,但對區域性變數只可以訪問,若想修改的話,我們可以在宣告區域性變數的時候加上關鍵字__block
程式碼如下:
1 |
__block int blockVar = 0; |
Block進階 參考部落格:http://www.cnblogs.com/NarutoYq/
下面的這些內容是參考上面的部落格進一步學習的Block的內容,程式碼是參考這上面的部落格自己寫的,也就是說下面的東西算是偽原創吧。小夥伴們如果沒大看懂下面的東西,請去上面的部落格中進一部的瞭解一下block.
1.區域性變數可變物件和不可變物件在block中的引用
下面會提供一部程式碼,這部分程式碼的功能是定義兩個區域性變數,一個是可變物件,一個是不可變物件,然後再定義一個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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
void blockTest1() { //定義兩個變數一個是可變的一個是不可變的 NSString *str1 = @"str1"; NSMutableString *str2 = [NSMutableString stringWithFormat:@"str2"]; //初始值 NSLog(@"兩個字串的初始值和初始地址"); NSLog(@"str1 = %@, str1_p = %p", str1, str1); NSLog(@"str2 = %@, str2_p = %p", str2, str2); //定義block在block中輸出連個變數的值和引數 void (^myBlock) () = ^() { NSLog(@"******************************************"); NSLog(@"在block塊中輸出區域性變數的可變和不可變變數"); NSLog(@"str1 = %@, str1_p = %p", str1, str1); NSLog(@"str2 = %@, str2_p = %p", str2, str2); }; //修改前賦值 str1 = @"str1_update"; [str2 appendString:@"_update"]; NSLog(@"******************************************"); NSLog(@"輸出修改後的值和地址"); NSLog(@"str1 = %@, str1_p = %p", str1, str1); NSLog(@"str2 = %@, str2_p = %p", str2, str2); //呼叫block myBlock(); NSLog(@"******************************************"); NSLog(@"呼叫block後的值和地址"); NSLog(@"str1 = %@, str1_p = %p", str1, str1); NSLog(@"str2 = %@, str2_p = %p", str2, str2); } |
程式碼說明:給定義的各一個可變和不可變的物件一個初始值,然後在呼叫程式碼塊的時候修改兩個區域性變數的值,然後再程式碼塊中顯示變數的值。
執行結果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
2014-08-10 13:30:25.710 Memory[1074:303] 兩個字串的初始值和初始地址 2014-08-10 13:30:25.711 Memory[1074:303] str1 = str1, str1_p = 0x100005ef0 2014-08-10 13:30:25.712 Memory[1074:303] str2 = str2, str2_p = 0x100204330 2014-08-10 13:30:25.712 Memory[1074:303] ****************************************** 2014-08-10 13:30:25.712 Memory[1074:303] 輸出修改後的值和地址 2014-08-10 13:30:25.713 Memory[1074:303] str1 = str1_update, str1_p = 0x100005fd0 2014-08-10 13:30:25.713 Memory[1074:303] str2 = str2_update, str2_p = 0x100204330 2014-08-10 13:30:25.713 Memory[1074:303] ****************************************** 2014-08-10 13:30:25.714 Memory[1074:303] 在block塊中輸出區域性變數的可變和不可變變數 2014-08-10 13:30:25.714 Memory[1074:303] str1 = str1, str1_p = 0x100005ef0 2014-08-10 13:30:25.714 Memory[1074:303] str2 = str2_update, str2_p = 0x100204330 2014-08-10 13:30:25.714 Memory[1074:303] ****************************************** 2014-08-10 13:30:25.715 Memory[1074:303] 呼叫block後的值和地址 2014-08-10 13:30:25.715 Memory[1074:303] str1 = str1_update, str1_p = 0x100005fd0 2014-08-10 13:30:25.715 Memory[1074:303] str2 = str2_update, str2_p = 0x100204330 |
從上面的輸出結果我們可以看到,在程式碼塊中輸出的不可變物件是原有的值,而不是我們改後的值,地址也是初始的地址。而對於可變物件,值是我們修改後的值,而地址使用原有的地址。如果要想block和不可變區域性變數繫結的話,我們要加上_block
還是引用上面部落格中的一段話來做一下總結吧:
-
對值型別的修改,如果block初始化後,無法同步到block內部
-
對於引用型別的修改,如果block初始化後,修改指標指向,即指向另外一塊記憶體,這樣也是無法同步到block內部
-
對於引用型別的修改,如果block初始化後,對指標指向的記憶體進行修改,即NSMutableArray add 、remove操作,這樣是可以用同步到block內部,但block內部同樣無法修改。
2.成員變數在block中的使用
成員變數在block中的使用是加上self->a使用的,所以在宣告成員變數的時候加不加__block,在成員函式中的程式碼塊中都可以訪問修改;
程式碼走起:
interface:
1 2 3 4 5 6 7 8 9 10 |
@interface BlockTest : NSObject //宣告兩個成員變數一個用__block 和 不用__block修飾觀察其變化 { __block NSString *hasBlock; NSString *noBlock; } -(void)test; @end |
方法的實現:
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 34 |
@implementation BlockTest -(void)test { //分別給兩個成員變數賦初始值 hasBlock = @"ludashi"; noBlock = @"ludashi"; NSLog(@"hasBlock = %<a href='http://www.jobbole.com/members/uz441800'>@,</a> hasBlock_p = %p", hasBlock, hasBlock); NSLog(@" noBlock = %@, noBlock_p = %p", noBlock, noBlock); //定義block void (^myBlock)() = ^() { //修改加__block的成員變數的值 hasBlock = @"ludashi_update"; NSLog(@"block中輸出的內容"); NSLog(@"hasBlock = %<a href='http://www.jobbole.com/members/uz441800'>@,</a> hasBlock_p = %p", hasBlock, hasBlock); NSLog(@" noBlock = %@, noBlock_p = %p", noBlock, noBlock); }; //改變noBlock的值 noBlock = @"ludashi_update"; NSLog(@"更新後的值"); NSLog(@"hasBlock = %<a href='http://www.jobbole.com/members/uz441800'>@,</a> hasBlock_p = %p", hasBlock, hasBlock); NSLog(@" noBlock = %@, noBlock_p = %p", noBlock, noBlock); //呼叫block myBlock(); //呼叫block後的值 NSLog(@"呼叫myBlock後的值"); NSLog(@"hasBlock = %<a href='http://www.jobbole.com/members/uz441800'>@,</a> hasBlock_p = %p", hasBlock, hasBlock); NSLog(@" noBlock = %@, noBlock_p = %p", noBlock, noBlock); } @end |
輸出結果:
1 2 3 4 5 6 7 8 9 10 11 |
2014-08-10 16:32:42.497 Memory[1349:303] hasBlock = ludashi, hasBlock_p = 0x100006188 2014-08-10 16:32:42.499 Memory[1349:303] noBlock = ludashi, noBlock_p = 0x100006188 2014-08-10 16:32:42.499 Memory[1349:303] 更新後的值 2014-08-10 16:32:42.500 Memory[1349:303] hasBlock = ludashi, hasBlock_p = 0x100006188 2014-08-10 16:32:42.500 Memory[1349:303] noBlock = ludashi_update, noBlock_p = 0x100006828 2014-08-10 16:32:42.500 Memory[1349:303] block中輸出的內容 2014-08-10 16:32:42.501 Memory[1349:303] hasBlock = ludashi_update, hasBlock_p = 0x100006828 2014-08-10 16:32:42.501 Memory[1349:303] noBlock = ludashi_update, noBlock_p = 0x100006828 2014-08-10 16:32:42.501 Memory[1349:303] 呼叫myBlock後的值 2014-08-10 16:32:42.502 Memory[1349:303] hasBlock = ludashi_update, hasBlock_p = 0x100006828 2014-08-10 16:32:42.502 Memory[1349:303] noBlock = ludashi_update, noBlock_p = 0x100006828 |
總結:
-
對於一個、多個成員變數,不管是否用__block修飾(用不用都沒任何影響),block結構體會生成一個成員 :self,並且會引用成員變數所屬的物件例項 self。
-
對於成員變數的修改都是通過物件self指標引用來實現的。
-
block內部對於成員變數的訪問也是通過block結構體物件的成員self 指標引用來實現的。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!