2013-07-23 77 views
10

我知道一個Objective-C塊可以捕獲並設置其封閉範圍之外的變量值。它是如何做到的?塊如何捕獲其封閉範圍之外的變量?

+1

我認爲它基本上對待它,就好像外部變量不超出範圍時,他們被定義的範圍結束,使用塊很多,但我不會算作自己的專家 – Fonix

回答

5

在塊對象的代碼體中,可以用五種不同的方式處理變量。

您可以參考三個標準類型的變量,就像你從一個函數將:

  • 全局變量,包括靜態當地人
  • 全局函數(這在技術上不變量)
  • 地方來自封閉示波器的變量和參數

塊還支持兩種其他類型的變量:

  1. 在功能級別是__block變量。這些在塊(和封閉範圍)內是可變的,並且如果任何引用塊被複制到堆中,它們將被保留。

  2. const進口。

最後,方法的實現中,塊可以參考物鏡-C實例變量 - 請參見對象和塊的變量。

以下規則適用於一個塊中使用的變量:

  1. 全局變量是可訪問的,包括存在的封閉詞法範圍內的靜態變量。

  2. 傳遞給塊的參數是可訪問的(就像函數的參數一樣)。

  3. 將封閉詞法作用域本地的棧(非靜態)變量作爲const變量捕獲。

    它們的值取自程序中塊表達式的位置。在嵌套塊中,該值是從最近的封閉範圍捕獲的。

  4. __block存儲修飾符聲明的封閉詞法範圍的局部變量由引用提供,因此可以修改。

    任何更改都反映在封閉詞法作用域中,包括在同一個封閉詞法作用域內定義的任何其他塊。這些在__block存儲類型中有更詳細的討論。

  5. 在塊的詞法範圍內聲明的局部變量,其行爲與函數中的局部變量完全相同。

塊的每次調用都會提供該變量的新副本。這些變量可以用作const或塊中封閉塊中的引用變量。

從這裏:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/Blocks/Articles/bxVariables.html

+4

還有一個實例變量參考在塊的範圍內意味着強烈地指向'self',並且實例變量將反映在塊執行時出現的任何值。這可能會讓人困惑。 – bbum

+1

這個問題問塊如何管理捕捉變量。不是捕獲變量的語義。 – newacct

19

它實際上相當簡單,在鏘的模塊實現規格說明,在"Imported Variables"部分。

當編譯器遇到一個像座:

^{ if(numBalloons > numClowns) abort(); } 

它會創建一個文本結構,包括 - 除其他事項外 - 在這裏是很重要的兩個元素。有一個指向塊中可執行代碼的函數指針,並且在塊內引用每個變量的const字段。事情是這樣的:

struct __block_literal_1 { 
    /* other fields */ 
    void (*invoke)(struct __block_literal_1 *); 
    /* ... */ 
    const int numBalloons; 
    const int numClowns; 
}; 

注意,invoke功能將一個指向真實被定義在這裏的那種結構;也就是說,Block在執行代碼時自動進入。因此,代碼可以訪問結構的成員。

權的聲明之後,編譯器創建的模塊,簡單的使用引用變量初始化在struct正確的字段的定義:

struct __block_literal_1 __block_literal_1 = { 
    /* Other fields */ 
    __block_invoke_2, /* This function was also created by the compiler. */ 
    /* ... */ 
    numBalloons, /* These two are the exact same variables as */ 
    numClowns  /* those referred to in the Block literal that you wrote. * 
}; 

然後,invoke函數內部,引用到捕獲的變量與結構的其他任何成員一樣,the_block->numBalloons

對象類型變量的情況稍微複雜一點,但同樣的原則適用。

0

基本上,對於每個捕獲的本地變量,塊「對象」在塊對象中包含一個變量(如塊對象的「實例變量」)。 (Josh Caswell的答案提供了有關如何實現的更多細節。)創建塊時,此時每個捕獲的局部變量的值將被複制到塊內的相應變量中。只要變量在塊內部使用,它就會在塊內使用該變量。