天天看點

Objective-C中的Block(閉包)

        學習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

);

            無傳回值的 

void

(^myBlock)(

int

int

);

          2.給block塊指派

           給聲明好的block,指派。block的值就是個函數體,給block塊指派有兩種方式,一個在聲明的時候指派,一個是先聲明在指派。

            先聲明再指派

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);

};

            在聲明的時候指派

int

(^sumBlock) (

int

int

) = ^(

int

a, 

int

b)

{

int

sum = a + b;

return

sum;

};

          3.調用block 

         block的使用和普通函數的使用相同,調用方法如下:

//調用代碼塊并接收傳回值

int

sum = sumBlock(20, 30);

         4.把block當做參數傳入函數

//把代碼塊作為函數參數

void

blockFunction(

int

(^myBlock)(

int

int

))

{

int

sum = myBlock(10,20);

NSLog(@

"fun_sum = %d"

, sum);

}  

        5.在代碼塊中使用局部變量和全局變量

            在block中可以和對全局變量進行通路和修改,但對局部變量隻可以通路,若想修改的話,我們可以在聲明局部變量的時候加上關鍵字__block

            代碼如下:

__block 

int

blockVar = 0;

        Block進階 參考部落格:http://www.cnblogs.com/NarutoYq/

        下面的這些内容是參考上面的部落格進一步學習的Block的内容,代碼是參考這上面的部落格自己寫的,也就是說下面的東西算是僞原創吧。小夥伴們如果沒大看懂下面的東西,請去上面的部落格中進一部的了解一下block.

        1.局部變量可變對象和不可變對象在block中的引用

            下面會提供一部代碼,這部分代碼的功能是定義兩個局部變量,一個是可變對象,一個是不可變對象,然後再定義一個Block, 在block中引用兩個局部變量。上面提到了在代碼塊中可以引用局部變量但是不可以更改其值,除非在聲明的時候加上__block關鍵字。

            測試代碼如下:

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

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);

}

        代碼說明:給定義的各一個可變和不可變的對象一個初始值,然後在調用代碼塊的時候修改兩個局部變量的值,然後再代碼塊中顯示變量的值。

        運作結果如下:

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

        還是引用上面部落格中的一段話來做一下總結吧:

  1. 對值類型的修改,如果block初始化後,無法同步到block内部
  2. 對于引用類型的修改,如果block初始化後,修改指針指向,即指向另外一塊記憶體,這樣也是無法同步到block内部
  3. 對于引用類型的修改,如果block初始化後,對指針指向的記憶體進行修改,即NSMutableArray add 、remove操作,這樣是可以用同步到block内部,但block内部同樣無法修改。

     2.成員變量在block中的使用

    ​    ​成員變量在block中的使用是加上self->a使用的,是以在聲明成員變量的時候加不加__block,在成員函數中的代碼塊中都可以通路修改;

    ​    ​代碼走起:

    ​    ​interface:

@interface BlockTest : NSObject

//聲明兩個成員變量一個用__block 和 不用__block修飾觀察其變化

{

__block NSString *hasBlock;

NSString *noBlock;

}

-(

void

)test;

@end

    ​方法的實作:

@implementation BlockTest

-(

void

)test

{

//分别給兩個成員變量賦初始值

hasBlock = @

"ludashi"

;

noBlock = @

"ludashi"

;

NSLog(@

"hasBlock = %@, hasBlock_p = %p"

, hasBlock, hasBlock);

NSLog(@

" noBlock = %@,  noBlock_p = %p"

, noBlock, noBlock);

//定義block

void

(^myBlock)() = ^()

{

//修改加__block的成員變量的值

hasBlock = @

"ludashi_update"

;

NSLog(@

"block中輸出的内容"

);

NSLog(@

"hasBlock = %@, hasBlock_p = %p"

, hasBlock, hasBlock);

NSLog(@

" noBlock = %@,  noBlock_p = %p"

, noBlock, noBlock);

};

//改變noBlock的值

noBlock = @

"ludashi_update"

;

NSLog(@

"更新後的值"

);

NSLog(@

"hasBlock = %@, hasBlock_p = %p"

, hasBlock, hasBlock);

NSLog(@

" noBlock = %@,  noBlock_p = %p"

, noBlock, noBlock);

//調用block

myBlock();

//調用block後的值

NSLog(@

"調用myBlock後的值"

);

NSLog(@

"hasBlock = %@, hasBlock_p = %p"

, hasBlock, hasBlock);

NSLog(@

" noBlock = %@,  noBlock_p = %p"

, noBlock, noBlock);

}

@end

    ​輸出結果:

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

總結:

  1. 對于一個、多個成員變量,不管是否用__block修飾(用不用都沒任何影響),block結構體會生成一個成員 :self,并且會引用成員變量所屬的對象執行個體 self。
  2. 對于成員變量的修改都是通過對象self指針引用來實作的。
  3. block内部對于成員變量的通路也是通過block結構體對象的成員self 指針引用來實作的。

作者:青玉伏案

出處:http://www.cnblogs.com/ludashi/

本文版權歸作者和共部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

如果文中有什麼錯誤,歡迎指出。以免更多的人被誤導。

收履歷:某網際網路公司,招聘iOS/Android靠譜工程師,入職後,可内部聯系樓主,有小禮品贈送,有意者可郵箱投遞履歷:[email protected]