程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言入門知識 >> OC教程6

OC教程6

編輯:C語言入門知識

OC6-代碼塊回調

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

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

    1,代碼塊機制

    蘋果公司在iOS4 SDK中首次支持代碼塊機制,隨後代碼塊機制被廣泛應用於各種編碼場景,最常見的為回調機制,也成為Block回調。

    代碼塊也稱Block。是封裝代碼的一種機制,也可以稱為匿名函數。

    使用這種機制可以將一段代碼放入一個Block變量中進行存儲,該變量可以作為參數進行傳遞,也可以通過該變量調用其存儲的代碼。

    2,Block變量類型

    在OC語法中,創建一個變量首先要明確其類型。Block作為一個可以儲存代碼的變量,其類型相對特殊。

    確定block變量的類型有兩個因素:

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

      只要這兩個因素一樣,我們就可以說是相同的block類型。

      現在我們舉一個簡單的例子,創建一個儲存沒有返回值,沒有輸入參數的代碼的block類型。

      void (^ varBlock)(void);
      

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

      如果想要儲存有返回值,有輸入參數的代碼,同樣可以聲明響應的block變量進行使用。

      int (^ varBlock1)(int a,int b);
      

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

      Block變量類型較為復雜,如果直接用這種方式進行聲明變量十分容易儲存。通常我們用typedef關鍵字將Block類型重命名,然後用相對簡單的類型名進行聲明變量的工作。

      typedef void (^ BlockType1)(void);
      
      BlockType1 var1;//var1與varBlock1為同一類型
      

      3,Block代碼封裝及調用

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

      typedef void (^ BlockType1)(void);
      
      BlockType1 var1;
      
      var1 = ^(){NSLog(@"test")};
      

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

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

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

        typedef int (^BlockType)(int a,int b);
        
        BlockType varBlock;
        
        varBlock = ^(int a,int b){return a+b;};
        

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

        int a = 4;
        int b = 6;
        int sum = varBlock(a,b);
        NSLog(@"sum = %d",sum);//輸出結果為10
        

        Block變量也可以給同類型的變量賦值

        BlockType varBlockTemp;
        varBlockTemp = varBlock;
        int sum = varBlockTemp(1,2);
        NSLog(@"sum = %d",sum);//輸出結果為3
        

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

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

        通過Block對象將代碼進行封裝的同時,有一個非常關鍵的問題我們需要明確討論,即Block變量對普通變量作用域的影響。

        通過一個簡單案例來因此這個問題。見如下代碼:

        main()
        {
            {
                int a = 1;
                {
                    a = 2;
                }
                //此處輸出a的值為2
            }
            //此處已經超出變量a的作用域,討論a的值無意義。
        }
        

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

        typedef void (^ BlockType)(void);
        
        BlockType var;
        
        void fun1()
        {
            int a = 10;
            var = ^(){NSLog(@"a = %d",a)};
        }
        
        void fun2()
        {
            var();
        }
        
        main()
        {
            fun1();
            fun2();
        }
        

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

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

        但此處情況特殊,變量a被block變量var所使用,所以var變量將a進行了一個復制操作,也就是我們在var的代碼裡面使用的a其實是a的副本。

        我們看下面的代碼:

        typedef void (^ BlockType)(void);
        
        BlockType var;
        
        void fun1()
        {
            int a = 10;
            var = ^(){NSLog(@"a = %d",a)};
            a = 20;
        }
        
        void fun2()
        {
            var();
        }
        
        main()
        {
            fun1();
            fun2();
        }
        

        這段代碼的輸出和上一段代碼一樣,不會因為fun1函數中a的值發生變化而導致block裡面的a的值發生變化。原因是Block變量在使用局部變量是,會對局部變量進行一個復制操作,block變量中儲存的代碼使用的時局部變量的副本。

        但是在某些特殊場合,我們需要改變局部變量可以引起block變量中代碼的變化。這時候我們需要使用block全景變量。

        block全景變量通過:__block來聲明。

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

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

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

        4,Block回調接口使用

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

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

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

          這裡仍然用之前的開關最為例子:

          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
          

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

          @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
          

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved