代碼塊(Block)回調通常闡述

本章教程主要對代碼塊回調模式進行講解,已經分析其餘回調的各類優缺點和適合的使用場景。html

  • 代碼塊機制
  • Block變量類型
  • Block代碼封裝及調用
  • Block變量對普通變量做用域的影響
  • Block回調接口使用
  • 0、Block簡介

    Block塊是封裝工做單元的對象,是能夠在任什麼時候間執行的代碼段。其本質上是可移植的匿名函數,能夠做爲方法和函數的參數傳入,能夠從方法和函數中返回。—(翻譯自官方文檔)java

    塊是對C語言的一種擴展,它並未做爲標準的ANSI C所定義的部分,而是有蘋果公司添加到語言中的。塊看起來更像是函數,能夠給塊傳遞參數,塊也能夠具備返回值。編程

    1,代碼塊機制

    蘋果公司在iOS4 SDK中首次支持代碼塊機制,隨後代碼塊機制被普遍應用於各類編碼場景,最多見的爲回調機制,也成爲Block回調。函數

    代碼塊也稱Block。是封裝代碼的一種機制,也能夠稱爲匿名函數。編碼

    使用這種機制能夠將一段代碼放入一個Block變量中進行存儲,該變量能夠做爲參數進行傳遞,也能夠經過該變量調用其存儲的代碼。atom

    2,Block變量類型

    在OC語法中,建立一個變量首先要明確其類型。Block做爲一個能夠儲存代碼的變量,其類型相對特殊。spa

    肯定block變量的類型有兩個因素:翻譯

    • 儲存代碼的返回值類型
    • 儲存代碼的參數列表

      只要這兩個因素同樣,咱們就能夠說是相同的block類型。code

      如今咱們舉一個簡單的例子,建立一個儲存沒有返回值,沒有輸入參數的代碼的block類型。htm

      ?
      1
      2
      <code> void (^ varBlock)( void );
      </code>

      上面的代碼聲明瞭一個block變量,變量名爲varBlock,其儲存代碼類型爲沒有返回值,沒有輸入參數。

      若是想要儲存有返回值,有輸入參數的代碼,一樣能夠聲明響應的block變量進行使用。

      ?
      1
      2
      <code> int (^ varBlock1)( int a, int b);
      </code>

      上面的代碼聲明瞭一個block變量,變量名爲varBlock1,其儲存代碼類型爲int型返回值,有兩個int型參數。

      Block變量類型較爲複雜,若是直接用這種方式進行聲明變量十分容易儲存。一般咱們用typedef關鍵字將Block類型重命名,而後用相對簡單的類型名進行聲明變量的工做。

      ?
      1
      2
      3
      4
      <code>typedef void (^ BlockType1)( void );
       
      BlockType1 var1; //var1與varBlock1爲同一類型
      </code>

      3,Block代碼封裝及調用

      有了Block變量,下面咱們就要給變量賦值。

      ?
      1
      2
      3
      4
      5
      6
      <code>typedef void (^ BlockType1)( void );
       
      BlockType1 var1;
       
      var1 = ^(){NSLog(@ "test" )};
      </code>

      經過上述語法格式將代碼封裝在大括號內,並用var1變量進行儲存。封裝代碼的過程當中要注意一下幾點:

      • ^符號開始爲Block封裝過程。
      • ^後面的小括號中寫這段代碼須要的參數。該參數有調用者進行賦值。
      • 小括號後面的大括號中寫要封裝的代碼,且代碼可使用小括號中的參數。

        下面舉一個求兩個數的和的代碼封裝過程。

        ?
        1
        2
        3
        4
        5
        6
        <code>typedef int (^BlockType)( int a, int b);
         
        BlockType varBlock;
         
        varBlock = ^( int a, int b){ return a+b;};
        </code>

        將代碼存入varBlock變量中後,即可以使用該變量調用代碼。

        ?
        1
        2
        3
        4
        5
        <code> int a = 4 ;
        int b = 6 ;
        int sum = varBlock(a,b);
        NSLog(@ "sum = %d" ,sum); //輸出結果爲10
        </code>

        Block變量也能夠給同類型的變量賦值

        ?
        1
        2
        3
        4
        5
        <code>BlockType varBlockTemp;
        varBlockTemp = varBlock;
        int sum = varBlockTemp( 1 , 2 );
        NSLog(@ "sum = %d" ,sum); //輸出結果爲3
        </code>

        將一段代碼當作一個變量進行傳遞,Block這樣的特性極大的方便了咱們以後的編碼工做

        3,Block變量對普通變量做用域的影響

        經過Block對象將代碼進行封裝的同時,有一個很是關鍵的問題咱們須要明確討論,即Block變量對普通變量做用域的影響。

        經過一個簡單案例來所以這個問題。見以下代碼:

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        <code>main()
        {
             {
                 int a = 1 ;
                 {
                     a = 2 ;
                 }
                 //此處輸出a的值爲2
             }
             //此處已經超出變量a的做用域,討論a的值無心義。
        }
        </code>

        這段代碼很簡單,經過大擴展來表示變量的做用域範圍。再看下面代碼:

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        <code>typedef void (^ BlockType)( void );
         
        BlockType var;
         
        void fun1()
        {
             int a = 10 ;
             var = ^(){NSLog(@ "a = %d" ,a)};
        }
         
        void fun2()
        {
             var();
        }
         
        main()
        {
             fun1();
             fun2();
        }
        </code>

        在fun2函數中調用var變量時,運行的是fun1中存入var變量的代碼,且代碼中的使用的變量a也是fun1中的局部變量。

        正常狀態下,變量a的做用域在fun1函數體大括號內。在函數體大括號外面使用a是沒有意義的。

        但此處狀況特殊,變量a被block變量var所使用,因此var變量將a進行了一個複製操做,也就是咱們在var的代碼裏面使用的a實際上是a的副本。

        咱們看下面的代碼:

        ?
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        <code>typedef void (^ BlockType)( void );
         
        BlockType var;
         
        void fun1()
        {
             int a = 10 ;
             var = ^(){NSLog(@ "a = %d" ,a)};
             a = 20 ;
        }
         
        void fun2()
        {
             var();
        }
         
        main()
        {
             fun1();
             fun2();
        }
        </code>

        這段代碼的輸出和上一段代碼同樣,不會由於fun1函數中a的值發生變化而致使block裏面的a的值發生變化。緣由是Block變量在使用局部變量是,會對局部變量進行一個複製操做,block變量中儲存的代碼使用的時局部變量的副本。

        可是在某些特殊場合,咱們須要改變局部變量能夠引發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
        <code>typedef void (^ BlockType)( void );
         
        BlockType var;
         
        void fun1()
        {
             __block int a = 10 ;
             var = ^(){NSLog(@ "a = %d" ,a)};
             a = 20 ;
        }
         
        void fun2()
        {
             var();
        }
         
        main()
        {
             fun1();
             fun2();
        }
        </code>

        上文代碼中,fun1中的變量a被block全景變量標識符所修飾,即變量a成爲一個block全景變量。

        其做用是,此時block封裝代碼時使用a變量,不會進行復制操做,也就表示,局部變量a與block代碼中的a爲同一個變量。因此,當前代碼的運行結果爲 a = 20。

        4,Block回調接口使用

        回調的本質爲控件反饋自身信息,在面向對象編程中,控件須要調用方法反饋自身信息,而方法必須從屬某個對象。因此以前的回調接口必須設置兩個參數,一個反饋對象,一個反饋方法。

        • 在目標動做中,反饋對象爲target,反饋方法爲action,一個SEL類型的變量。
        • 在委託回調中,反饋對象爲delegate,反饋方法爲組件協議中聲明的方法。

          在Block回調中,因Block機制能夠直接將代碼封裝如一個變量中,並且這個變量能夠當作參數進行傳遞。利用這個機制,組件能夠保存這段代碼,在觸發事件的時候直接調用此段代碼,不須要設置反饋對象和反饋方法。

          這裏仍然用以前的開關最爲例子:

          ?
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          <code>typedef enum : NSUInteger {
               SwitchStateOff, //default
               SwitchStateOn,
          } SwitchState;
           
          typedef void (^SBlockType)(SwitchState state);
           
          @interface SwitchB : NSObject
           
          @property (nonatomic,assign,readonly)SwitchState currentState;
           
          @property (nonatomic,strong)SBlockType changeStateBlockHandle;
           
          @end
          </code>

          聲明中的changeStateBlockHandle屬性就是保存回調代碼。設置回調,只須要將此屬性賦值就可。

          ?
          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
          <code> @interface Room : NSObject
          @property (strong, nonatomic) Light *lightA;
          @property (strong, nonatomic) SwitchB *s;
           
          @end
           
           
          @implementation Room
          - (instancetype)init
          {
               self = [ super init];
               if (self) {
                   self.lightA = [[Light alloc] init];
                   self.s = [[SwitchB alloc] init];
           
                   __weak __block Room * copy_self = self; //打破強引用循環,後續章節會展開講解
           
                   self.s.changeStateBlockHandle = ^(SwitchState state)
                   {
                       if (state == SwitchStateOff)
                       {
                           [self.lightA turnOff];
                       }
                       else
                       {
                           [self.lightA turnOn];
                       }
                   };
               }
               return self;
          }
          @end
          </code>

          當開關的狀態發生改變時,開關須要將自身狀態反饋給使用者。當使用Block回調接口的組件時,須要將回調代碼直接封裝,賦值給組件響應的Block類型的屬性便可。當狀態改變時,封裝的代碼便被組件調用。

相關文章
相關標籤/搜索