iOS——Block筆記

一、block的基本概念和語法                                                            html

  • 基本概念ios

    block叫代碼塊、閉包或者匿名函數。包含返回值類型、block標記、名稱、參數和主體,相似C語言的函數。它本質上是可移植的匿名函數,可做爲方法和函數的參數傳入,或可從方法和函數中返回。git

  • 語法結構github

    返回類型(^名稱)(參數...) = ^(參數) { 主體... };算法


   /*例子:這是一個返回值類型爲 int 名稱爲fristBlock 帶有一個int類型參數的block。*/
    
    int (^fristBlock)(int) = ^(int a) {
        
        int b = a + 200;
        return b;
    };

  • Apple所推薦的block使用範圍包括如下幾個方面:多線程

    排序 ——在block內寫排序算法閉包

    View動畫 ——簡單明瞭的方式規定動畫app

    完成處理 ——當方法執行完畢後執行block代碼函數

    枚舉 ——經過block獲取枚舉對象或控制枚舉進程測試

    通知 ——當某事件發生後執行block內的代碼

    錯誤處理 ——當錯誤發生時執行block代碼

    GCD多線程 ——多線程控制

二、block的簡單使用                                                                            

  •   block也叫匿名函數,因此用法相似C語言函數。兩步:實現和調用


/*一、實現block*/
int b = 100;
void (^myBlock)(int a) = ^(int a) {
  
    // block 主體
    int c =  a + b;
    NSLog(@"c = %d",c);
};
    
/*二、調用block:block中的代碼必須調用才能起做用,相似c函數的調用*/
myBlock(200);	// 調用block


三、block做爲參數使用                                                                                         

  •   block做爲一個代碼塊能夠被用來做爲方法或者函數的參數。


*/ 調用方法*/
[self blockMethod:^(int a, int b) {

    NSLog(@"a + b = %d",a + b);
 }];

/*定義一個以block爲參數的方法*/
- (void)blockMethod:(void(^)(int a,int b)) block {

    block(100,200); // 調用
}


四、使用typedef定義block                                                                 

  • 因爲block的語法不是很簡潔,直接使用不是很方便。因此咱們使用時一般使用typedef關鍵字來定義block,達到使用的方便簡潔。


/*使用typedef定義一個沒有返回值帶有兩個int參數的block類型名稱爲TestBlock,後面就能夠直接使用TestBlock定義對應的block變量了*/

typedef void(^TestBlock)(int a, int b);

/*1.定義一個類型爲TestBlock的block。*/
TestBlock tBlock = ^(int a, int b){
        
    NSLog(@"a + b = %d",a + b);
};
tBlock(200,300);

/*2.使用TestBlock類型的block做爲參數*/
- (void)blockMethodTypedef:(TestBlock)block
{
    if(block) {
        block(100,200);
    }
}

/*3.使用TestBlock類型的block做爲返回值*/
- (TestBlock)returnBlockMethodTypedef
{
    return ^(int a, int b){
       
        NSLog(@"output two number %d and %d",a, b);
    };
}


五、block做爲屬性使用                                                                        

  • block代碼塊能夠做爲類的屬性使用,使用copy關鍵字修飾


/*>> .h文件中定義*/
typedef int(^SumBlock)(int a, int b);

@property(nonatomic, copy)SumBlock block;

/*>> .m文件中實現\調用*/
/*一、實現block*/
self.block = ^(int a,int b) {

    return a + b;
};
/*二、調用block,調用時先判斷block不爲nil再調用。由於block有可能沒實現*/
if (self.block) {

    int sum = self.block(100,200)    
    NSLog(@"sum = %d",sum );
}


六、block訪問使用局部變量                                                                  

  • block內部能夠使用外部的局部變量,可是不能修改。若是要想修改得使用__block關鍵字修飾變量。


/*例1. 訪問外部變量 */
    int num = 100;
    int(^numBlock)(int) = ^(int a){

        int result = num + a;
        // num += 100; 錯誤:block內只能訪問使用外部普通變量,可是不能修改。
        return result;
    };
    num = 0;
    NSLog(@"num = %d",num); // num = 0
    NSLog(@"numBlock = %d",numBlock(200));   // numBlock = 300

    
/*例2. 修改外部變量*/
   __block int var = 100;         // block變量
    int(^varBlock)(int) = ^(int a){

        var += a;                 // __block標記的變量block內部能夠修改
        return var;
    };
    var = 0;
    NSLog(@"var = %d",var); // var = 0
    NSLog(@"varBlock = %d",varBlock(200)); // varBlock = 200

  • 如上打印結果,numBlock = 300, varBlock = 200;

  • 解析:

    block使用外部普通局部變量時是將變量的值拷貝一份到block中,也就是說block內的num再也不是原來同一個num了,因此後面修改num的值不會影響block中num。

    使用__block修飾的變量在block中使用時不會進行拷貝,使用的是同一個。因此後面修改var的值會影響block中的var。

五、block內存管理                                                                              

  • block的類別:三種類型的block

    1. NSGlobalBlock 全局區的block,沒有使用任何外部變量的block

    2. NSStackBlock 棧區的block,使用了外部局部變量的block

    3. NSMallocBlock 堆區block,使用copy或Block_copy()拷貝後block就保持到了堆區中,須要管理內存。

  • block的拷貝:(MRC下)

    1. 使用copy或Block_copy()是將棧區的block拷貝到堆區中,引用計數不會+1。

    2. block被拷貝後會對使用到的外部局部變量對象引用計數+1,可是全局變量、實例變量、靜態變量、__block變量引用計數不會+1。

    3. block內使用了實例變量不會對實例引用計數+1,可是會對實例變量持有者應用計數+1。

    4. MRC下須要手動使用copy或者Block_copy()將block拷貝到堆區中,而後內存本身管理,使用Block_release()對應引用計數-1。

    5. ARC下不須要手動copy,當block使用了局部變量後會自動拷貝到堆區中,也就是說ARC下不須要手動對block進行copy和release。

  • 例1:block內使用的對象的引用計數。


  static NSObject *staticObj = nil;
  _instanceObj = [[NSObject alloc] init];  //  實例變量
  globalObj = [[NSObject alloc] init];     //  全局變量
  staticObj = [[NSObject alloc] init];     //  靜態變量

  NSObject *localObj = [[NSObject alloc] init]; // 局部變量
  __block NSObject *blockObj = [[NSObject alloc] init]; // __block變量

  self.block = ^{

      NSLog(@"%@",[_instanceObj retainCount]); // 1
      NSLog(@"%@",[globalObj retainCount]);    // 1
      NSLog(@"%@",[staticObj retainCount]);    // 1
      NSLog(@"%@",[localObj retainCount]);     // 2
      NSLog(@"%@",[blockObj retainCount]);     // 1
  };

  if (self.block) {

      _block();
  }

  • 例2:MRC下對block的拷貝


  int value = 100;
  void(^block)(int) = ^(int var){

      NSLog(@"result:%d",value + var);
  };
  NSLog(@"%@",block);         //<__NSStackBlock__: 0x7fff51896988>

  NSLog(@"引用計數:%ld",[block retainCount]); // 1

  /* 使用copy或者Block_copy()把棧區的block拷貝到堆區,引用計數不會改變。*/
  void(^mallocBlock)(int) = Block_copy(block);
  //void(^mallocBlock)(int) = [block copy];
  mallocBlock(200);

  NSLog(@"%@",mallocBlock);   // <__NSMallocBlock__: 0x7fa0ca617d10>
  NSLog(@"引用計數:%ld",[mallocBlock retainCount]); // 1

  Block_release(mallocBlock); //堆區的block須要管理內存。


六、block的循環引                                                                    

  • 描述:

    當一個對象持有了block後,block內部又使用了這個對象或者對象的實例變量、實例方法,這樣會使block也持有這個對象,這樣相互持有就是循環引用,從而致使對象沒法釋放引發內存泄露。

  • 解決:

    要解決循環引用只須要不要相互持有就好了,也就是說只須要一方持有就能夠不讓block再持有當前這個對象就OK。


  /*循環引用:self持有block,block又持有了self。*/
  self.block = ^{

      self.info = @"這裏會有循環引用";  // block內部使用了self得實例變量會retain self
      [self testMeethod];

  };


  /*1. MRC下解決循環引用:不要在block內部直接使用self,要使用__block修飾的self,這樣block內部就不會對self retain了。*/
  __block typeof(self)blockSelf = self;
  self.block = ^{

      blockSelf.info = @"這樣就沒有循環引用了";
      [_blockSelf testMethod];    

  };

  /*2. ARC下解決循環引用:使用__weak修飾self後,block就不會再持有self了。*/
  __weak typeof(self)weakSelf = self;
  self.block = ^{

      weakSelf.info = @"這樣就不會循環引用了";
      [weakSelf testMethod];
  };

  /*實例方法*/
  - (void)testMethod
  {
   NSLog(@"測試方法!");
  }


七、block實踐使用Demo(傳值 、事件處理)                               

Demo地址:https://github.com/fuqinglin/block-     

參考:

一、 http://blog.csdn.net/yin_xianwei/article/details/17998165


二、https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Articles/bxGettingStarted.html                                                                                 

相關文章
相關標籤/搜索