天天看點

Blocks Programming Topics

Blocks Programming Topics

介紹

    Block對象是一個c語言級别的文法和運作時特性,它和标準的C函數有相似之處,但是它除了可執行的代碼之外還包含了變量。變量既可以是自動記憶體管理的(棧區)也可以是手動記憶體管理的(堆區)。是以Block可以維持一組資料用來影響行為。

    你可以使用blocks組成函數表達式進而傳遞給API接口,并且可以可選存儲和多線程。Blocks用作回調函數尤為有用,因為block擁有着所有的可執行代碼和在執行區間用到的變量資料。

    Blocks在GCC編譯器中也是可用的,你可以在OS X 10.6之後或者iOS 4.0 或更高版本上使用。Blocks的運作時系統是開源的你可以在LLVM’s compiler-rt subproject repository處找到

    你需要讀一讀本文檔看看block到底是什麼和block到底怎樣和C C++ Objective-C使用。

     開始使用Blocks

    下面的部分使用練習的例子幫助你學會blocks

聲明和使用一個代碼塊Block

你可以使用^運算符聲明一個block變量并且辨別着一個block的開始,block的代碼塊在{}之間,例子如下int 

multiplier = 7;
int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
};
           
Blocks Programming Topics

    注意block中可以使用在同一作用域中定義的變量。

    如果你把block定義為一個變量,你可以在稍後像用函數一樣使用它。

int multiplier = 7;
  int (^myBlock)(int) = ^(int num) {
    return num * multiplier;
  };
  printf("%d", myBlock(3));
  // prints "21"
           

 直接使用Block

很多情況,你可能不希望聲明一個Block變量。而是僅僅在作為函數的block處内聯實作。下面的例子就是使用block作為最後一個參數。

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
           

 在Cocoa中使用Blocks

    Cocoa架構中有很多方法使用block作為一個參數,比較有代表性的就是執行一個對象集合上的操作,或者在一個操作完成後使用一個回調函數。下面的例子展示了在NSArray中的sortedArrayUsingComparator:方法上如何使用block。

這個方法有唯一的參數--block,為了說明,這種情況下block被定義為一個NSComparator的局部對象

NSArray *stringsArray = @[ @"string 1",
                             @"String 21",
                             @"string 12",
                             @"String 11",
                             @"String 02" ];
static NSStringCompareOptions comparisonOptions = NSCaseInsensitiveSearch |
  NSNumericSearch |
          NSWidthInsensitiveSearch | NSForcedOrderingSearch;
  NSLocale *currentLocale = [NSLocale currentLocale];
  NSComparator finderSortBlock = ^(id string1, id string2) {
      NSRange string1Range = NSMakeRange(0, [string1 length]);
      return [string1 compare:string2 options:comparisonOptions range:string1Range
  locale:currentLocale];
};
NSArray *finderSortArray = [stringsArray sortedArrayUsingComparator:finderSortBlock];NSLog(@"finderSortArray: %@", finderSortArray);
  /*
  Output:
  finderSortArray: (
      "string 1",
      "String 02",
      "String 11",
      "string 12",
      "String 21"
)*/ 
           

__block 變量

block一個強大的特性就是它可以修改同一作用域的變量。你可以用__block存儲類型說明符标志一個變量是可修改地。為了迎合 “Blocks withCocoa"的例子,你可以用block變量計數有多少字元串是相等的。

為了說明,在這種情況下,直接使用局部變量在block中是隻讀的。

NSArray *stringsArray = @[ @"string 1",
@"String 21", // <-
@"string 12",
@"String 11",
@"Strîng 21", // <-
@"Striñg 21", // <-
@"String 02" ];
NSLocale *currentLocale = [NSLocale currentLocale];
__block NSUInteger orderedSameCount = 0;
NSArray *diacriticInsensitiveSortArray = [stringsArray
sortedArrayUsingComparator:^(id string1, id string2) {
    NSRange string1Range = NSMakeRange(0, [string1 length]);
    NSComparisonResult comparisonResult = [string1 compare:string2
options:NSDiacriticInsensitiveSearch range:string1Range locale:currentLocale];
    if (comparisonResult == NSOrderedSame) {
        orderedSameCount++;
}
    return comparisonResult;
}];
NSLog(@"diacriticInsensitiveSortArray: %@", diacriticInsensitiveSortArray);
NSLog(@"orderedSameCount: %d", orderedSameCount);
/*Output:
diacriticInsensitiveSortArray: (
    "String 02",
"string 1",
"String 11",
"string 12",
"String 21",
"Str\U00eeng 21",
      "Stri\U00f1g 21"
  )
  orderedSameCount: 2
  */
           

概念回顧

Block對象提供了一種方法可以讓你建立一個hoc的函數體作為c或c派生語言(如:c++/ objective-c)的表達式。在其他的語言中,block對象有的叫做閉包(“closure”)。在這裡他們被成為block,這樣不會和标準c的屬于混淆。

Block 功能 

block就是一個内聯的代碼集合:

  • 擁有一個像函數一樣的參數清單
  • 可以有傳回值
  • 可以使用在同一作用域中聲明的變量
  • 可以可選的修改同一作用域的變量
  • 可以與其他定義在同一作用域的block共享修改變量
  • 在過了此作用域之後仍然可以修改此原先作用域的變量

你可以拷貝一個block甚至将它傳遞到其他線程中推遲執行(或者,在自己的線程中runloop)。編譯器和運作時系統安排所有在block中引用的變量,在block的所有拷貝的生命周期中都會存儲。盡管blocks在純C/C++中也可以用,但是block也是一個objective-c對象。

使用

Block作為一個封裝的工作單元,而且可能會并發執行,或者用作周遊集合,或者在一個其他的操作完成後作為一個回調函數。

Block作為替代傳統回到函數的有用的選擇,這裡列舉兩個原因:

  1. They allow you to write code at the point of(正要) invocation(調用) that is executed later in the context of the methodimplementation(實作).

    Block是以作為庫中很多方法的參數

  2. 他們允許你通路局部變量。

    Rather than using callbacks requiring a data structure that embodies all the contextual information youneed to perform an operation, you simply access local variables directly. 

聲明和建立Blocks

聲明一個block引用

Block變量保持引用到一個blocks。你可以聲明他們使用的文法類似于函數指針,除了你要使用^來替代*之外。block類型完全可以喝其餘的c語言基本類型互動,下面全是有效的block變量聲明:

void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
           

Blocks也支援可變的參數(...),如果一個block是無參數的,那麼必須要在參數清單處指明void類型。

Blocks被設計為一種完全安全的類型,它通過給編譯器一整套的中繼資料用來驗證使用Blocks,參數傳遞到blocks,和blocks的傳回值指派。

 You can cast a block reference to apointer of arbitrary type and vice versa. You cannot, however, dereference a block reference via the pointerdereference operator (*)—thus a block's size cannot be computed at compile time. 

    你也可以自己創造一種Block的類型,這樣你在多處使用Block将變得更簡單

typedef float (^MyBlockType)(float, float);
MyBlockType myFirstBlock = // ... ;
MyBlockType mySecondBlock = // ... ;
           

創造一個Block

使用 ^運算符辨別一個Block表達式的開始。它可能後面跟着一個空得參數清單。block的主體在{}之間。下面的例子定義了一個簡單地BLock并且給它指派--在這裡Block以;結尾

float (^oneFrom)(float);
oneFrom = ^(float aFloat) {
    float result = aFloat - 1.0;
    return result;
};
           

如果你沒有明确地定義一個block的表達式的傳回值(就是以^開頭的那個表達式),它會自動根據block的内容推測。如果你的參數清單是空的,那麼你可以在block表達式(^開頭的表達式)可以省略(void)。

如果block内部存在多個傳回值,那麼他們的類型必須比對。

 全局Blocks

 若是檔案級全局,你可以這樣使用block

#import <stdio.h>
int GlobalInt = 0;
int (^getGlobalInt)(void) = ^{ return GlobalInt; };
           

 Blocks和變量

 這部分文章描述blocks和變量的互動,包括記憶體的管理

 變量的類型

 在block對象代碼中,變量可能被分成5種:

 你可以使用3中标準的變量類型,就像在函數中一樣:

  •  全局變量,包括靜态局部變量
  •  全局函數
  •  局部變量和參數變量

 Blocks還支援兩種變量類型:

  1. At function level are __blockvariables. These are mutable within the block (and the enclosing scope)and are preserved if any referencing block is copied to the heap.
  2. constimports. 

 最後,在一個方法的實作裡,blocks可能使用Objective-C的執行個體變量。

  下面是用在Block裡的變量的規則

  1.   全局變量是可通路的,包括局部靜态變量
  2.   傳遞到block中的參數是可通路的(就像函數一樣)
  3.   Stack (non-static) variables local to the enclosing lexical scope are captured as const variables. Their values are taken at the point of the block expression within the program. In nested blocks, the valueis captured from the nearest enclosing scope. 
  4. Variables local to the enclosing lexical scope declared with the __block storage modifier are providedby reference and so are mutable. 
  5. Local variables declared within the lexical scope of the block, which behave exactly like local variables ina function.