iOS代碼塊block使用

     代碼塊的本質是和其餘的變量相似,不一樣的是,代碼塊存儲的數據是一個函數體。使用代碼塊,你能夠像調用其餘標準函數同樣的調用,能夠傳入參數,並獲得返回值。
     脫字符是代碼塊的語法標記。下圖表示代碼塊的定義。html

1.代碼塊的基本使用數組

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//無參數無返回值
void  (^myblock)() = ^()
{
     NSLog (@ "Hello, World!" );
};
myblock();
 
//帶參數無返回值
void  (^myblock2)( NSString  *string) = ^( NSString  *string){   NSLog (@ "%@" ,string);};
myblock2(@ "Hello, World myblock2!" );
 
  //無參數有返回值
int  (^myblocksss)() = ^( int  i){ return  12;};
int  c = myblocksss();
NSLog (@ "%i" ,c);
 
//有參數有返回值
int  (^myblock3)( int ) = ^( int  i){  return  12 * i; };
int  i = myblock3(3);
NSLog (@ "%i" ,i);

2,利用typedef爲Block進行重命名函數

使用typedef爲block進行一次重命名,方法跟爲函數指針進行重命名是同樣的:post

 

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
//  Copyright © 2016年 liujun. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
typedef  int  (^ MyBlock)( int  a, int  b);
 
int  main( int  argc,  const  char  * argv[]) {
     @autoreleasepool  {
         // insert code here...
         
         __block  int  n = 100;
         
         MyBlock block = ^( int  a, int  b)
         {
             n = 20;  //不過沒有用__block 修飾 代碼不會編譯經過
             
             return  n + a + b;
         };
         
         NSLog (@ "%i   %i" , n ,block(3,4));  //輸出結果  100   27
         
         NSLog (@ "%i   %i" , block(3,4) ,n); //輸出結果   27    20
         //以上輸出。說明代碼塊是在調用的時候纔會被執行
         
         NSLog (@ "Hello, World!" );
     }
     return  0;
}

  

3.Block在內存中的位置this

根據Block在內存中的位置分爲三種類型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:相似函數,位於text段;
NSStackBlock:位於棧內存,函數返回後Block將無效;
NSMallocBlock:位於堆內存。url

 

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
//  Copyright © 2016年 liujun. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
typedef  long  (^Sum)( int , int );
 
int  main( int  argc,  const  char  * argv[]) {
     @autoreleasepool  {
         // insert code here...v
         
         Sum sum1 = ^  long  ( int  a,  int  b) {
             return  a + b ;
         };
         NSLog (@ "sum1 = %@" , sum1); // 打印結果:sum1 = <__NSGlobalBlock__: 0x47d0>
         
         
         
         
         int  base = 100;
         Sum sum2 = ^  long  ( int  a,  int  b) {
             return  base + a + b;
         };
         NSLog (@ "sum2 = %@" , sum2);  // 打印結果:sum2 = <__NSMallocBlock__: 0xbfffddf8>
         
         
         
         Sum sum3 = [sum2  copy ];
         NSLog (@ "sum3 = %@" , sum3);  // 打印結果:sum3 = <__NSMallocBlock__: 0x902fda0>
         NSLog (@ "Hello, World!" );
     }
     return  0;
}

  NSGlobalBlock,咱們只要實現一個沒有對周圍變量沒有引用的Block,就會顯示爲是它。而若是其中加入了對定義環境變量的引用,就是NSStackBlock。那麼NSMallocBlock又是哪來的呢?malloc一詞其實你們都熟悉,就是在堆上分配動態內存時。沒錯,若是你對一個NSStackBlock對象使用了Block_copy()或者發送了copy消息,就會獲得NSMallocBlock。這一段中的幾項結論可從代碼實驗得出。spa

     也就獲得了下面對block的使用注意點。指針

     對於Global的Block,咱們無需多處理,不需retain和copy,由於即便你這樣作了,彷佛也不會有什麼兩樣。對於Stack的Block,若是不作任何操做,就會向上面所說,隨棧幀自生自滅。而若是想讓它得到比stack frame更久,那就調用Block_copy(),讓它搬家到堆內存上。而對於已經在堆上的block,也不要期望經過copy進行「真正的copy」,由於其引用到的變量仍然會是同一份,在這個意義上看,這裏的copy和retain的做用已經很是相似。code

4,外部參數在代碼塊的使用htm

blk1和blk2的區別在於:

blk1沒有使用Block之外的任何外部變量,Block不須要創建局部變量值的快照,這使blk1與通常函數沒有任何區別

blk2與blk1惟一不一樣是的使用了局部變量base,在定義(注意是「定義」,不是「運行」)blk2時,局部變量base當前值被copy到棧上,做爲常量供Block使用。執行下面代碼,結果是203,而不是204。

1
2
3
4
5
6
7
int  base = 100; 
   base += 100; 
   BlkSum sum = ^  long  ( int  a,  int  b) { 
     return  base + a + b; 
   }; 
   base++; 
   printf( "%ld" ,sum(1,2));

 

 在Block內變量base是隻讀的,若是想在Block內改變base的值,在定義base時要用 __block修飾:__block int base = 100;

1
2
3
4
5
6
7
8
9
__block  int  base = 100; 
base += 100; 
BlkSum sum = ^  long  ( int  a,  int  b) { 
   base += 10; 
   return  base + a + b; 
}; 
base++; 
printf( "%ld\n" ,sum(1,2)); 
printf( "%d\n" ,base);

     輸出將是214,211。Block中使用__block修飾的變量時,將取變量此刻運行時的值,而不是定義時的快照。這個例子中,執行sum(1,2)時,base將取base++以後的值,也就是201,再執行Blockbase+=10; base+a+b,運行結果是214。執行完Block時,base已經變成211了。

   

      static變量、全局變量 :若是把上個例子的base改爲全局的、或static。Block就能夠對他進行讀寫了。由於全局變量或靜態變量在內存中的地址是固定的,Block在讀取該變量值的時候是直接從其所在內存讀出,獲取到的是最新值,而不是在定義時copy的常量。

1
2
3
4
5
6
7
8
9
static  int  base = 100; 
BlkSum sum = ^  long  ( int  a,  int  b) { 
   base++; 
   return  base + a + b; 
}; 
base = 0; 
printf( "%d\n" , base); 
printf( "%ld\n" ,sum(1,2));  // 這裏輸出是3,而不是103 
printf( "%d\n" , base); 

  輸出結果是0 4 1,代表Block外部對base的更新會影響Block中的base的取值,一樣Block對base的更新也會影響Block外部的base值。

 

Block變量,被__block修飾的變量稱做Block變量。 基本類型的Block變量等效於全局變量、或靜態變量。

 

5,循環引用

     retain cycle問題的根源在於Block和obj可能會互相強引用,互相retain對方,這樣就致使了retain cycle,最後這個Block和obj就變成了孤島,誰也釋放不了誰。好比:

1
2
3
4
5
6
7
8
9
10
11
12
13
@implementation  TsetBlock 
   
-( id )init{ 
   
    if  ( self  = [superinit]) { 
        self .testStr =@ "中國"
         self .block = ^( NSString  *name,  NSString  *str){ 
            NSLog (@ "arr:%@" , self .testStr);  // 編譯警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle 
        }; 
   
    returnself; 
@end 

  網上大部分帖子都表述爲"block裏面引用了self致使循環引用",但事實真的是如此嗎?我表示懷疑,其實這種說法是不嚴謹的,不必定要顯式地出現"self"字眼纔會引發循環引用。咱們改一下代碼,不經過屬性self.testStr去訪問String變量,而是經過實例變量_testStr去訪問,以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@implementation  TsetBlock 
   
-( id )init{ 
   
    if  ( self  = [superinit]) { 
        self .testStr =@ "中國"
         self .block = ^( NSString  *name, NSString  *str){ 
            NSLog (@ "arr:%@" , _testStr);  // 一樣出現: Capturing 'self' strongly in this block is likely to lead to a retain cycle 
        }; 
   
    returnself; 
@end 

  能夠發現:
即便在你的block代碼中沒有顯式地出現"self",也會出現循環引用!只要你在block裏用到了self所擁有的東西!

要分兩種環境去解決:在ARC下不用__block ,而是用 __weak 爲了不出現循環引用

1.ARC:用__week

__weaktypeof (self)  weakSelf = self; 或者

__weak someClass *weakSelf = self;


2.MRC:用__block ,__block修飾的變量在Block copy時是不會retain的,因此,也能夠作到破解循環引用。
__block someClass *blockSelf = self;

 

bloack的 retain、copy、release 操做

對Block不論是retain、copy、release都不會改變引用計數retainCount,retainCount始終是1;

NSGlobalBlock:retain、copy、release操做都無效;
NSStackBlock:retain、release操做無效,必須注意的是,NSStackBlock在函數返回後,Block內存將被回收。即便retain也沒用。容易犯的錯誤是[[mutableAarry addObject:stackBlock],在函數出棧後,從mutableAarry中取到的stackBlock已經被回收,變成了野指針。正確的作法是先將stackBlock copy到堆上,而後加入數組:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy以後生成新的NSMallocBlock類型對象。
NSMallocBlock支持retain、release,雖然retainCount始終是1,但內存管理器中仍然會增長、減小計數。copy以後不會生成新的對象,只是增長了一次引用,相似retain;
儘可能不要對Block使用retain操做。

 

6.代碼塊的遞歸調用

代碼塊想要遞歸調用,代碼塊變量必須是全局變量或者是靜態變量,這樣在程序啓動的時候代碼塊變量就初始化了,能夠遞歸調用

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static  void  (^  const  myblock4)( int ) = ^( int  i)
 
         {
 
             if (i > 0)
 
             {
 
                 NSLog (@ "%i" ,i);
 
                 myblock4(i - 1);
 
             }
 
         };
相關文章
相關標籤/搜索