7

我熟悉__block語句,它使塊中的變量'可分配'。 但是我看到,當使用一些Objective-C功能使用塊作爲方法中的參數時,即使它們沒有用此__block語句聲明,也可以分配一些變量。iOS >>塊>>更改塊外部變量的值

下面是實施例2級的代碼:

[UIView animateWithDuration:2 animations:^ 
    { 
     self.animatedView.backgroundColor = [UIColor blueColor]; 
     self.animatedView.center = CGPointMake(100, 100); 
    }]; 

(animatedView是一個IBOutlet連接的簡單的UIView)。

int myInt = 10; 
    NSMutableString* mString = [NSMutableString stringWithString:@"Mutable Hello"]; 
    NSString* imString = @"Imutable Hello"; 

    void (^myBlock)(void) =^
    { 
     [mString appendString:@" Block"]; //working 
     imString = @"Imutable Hello Block"; //error 
     myInt = 11; //error 
    }; 

我的問題是:爲什麼我可以將值分配給UIView的實例屬性?

我沒有解決一個對象並改變它,就像我的mString。

我期望'中心'屬性的行爲就像我的myInt一樣,因爲它是直接訪問的C結構,而不是指向對象的指針。

我期望'backgroundColor'的行爲就像我的imString一樣,因爲它是一個指向一個新對象的對象的指針,不是嗎?

我無法在文檔中找到令人滿意的解釋......如果有人能提供一個,或者將我定位到一個,我將不勝感激。

回答

8

這是分配和使用之間的區別。用法是方法調用。您完全可以調用實例上的方法([mString appendString:@" Block"]; //working),但不能在未標記變量的情況下分配(imString = @"Imutable Hello Block"; //error),以告知編譯器它應該啓用它。

此代碼:

self.animatedView.backgroundColor = [UIColor blueColor]; 

還沒有真正的分配,這是一個「隱藏」的方法調用。點符號不是一個賦值,它是方法調用的語法糖。它實際上轉化爲:

[[self animatedView] setBackgroundColor:[UIColor blueColor]]; 

分配給本地變量和對象內的變量之間的區別是在內存中,它們駐留在的位置。基本上,它們會存在足夠長時間纔能有用。這是堆棧和堆上的數據之間的區別。

+0

讓「BOOL隱藏」的事實或「CGPoint中心」追究他們背後的ivars不會有什麼區別?這是否意味着它們是屬性(通過Setter方法訪問)會改變它們在內存中的分配方式?我也嘗試過在我的課堂上聲明的伊娃(而不是方法中的局部變量),它也起作用 - 而且它沒有通過任何方法。 –

+1

該方法是一個副作用(爲了更好的術語)。區別在於內存位置。局部變量在堆棧上創建,所以除非您告訴編譯器將它們複製到堆中,否則它們不可訪問。編譯器在塊內部提供數據時也使用了一些'技巧'(並且所採用的路由在指針和原語之間是不同的)。這是一個深入的主題,請閱讀此主題:http://stackoverflow.com/questions/15082265/why-is-a-block-variable-is-moved-to-the-heap-before-the-塊被複制 – Wain

+0

明白了。 Wain,你是男人!謝謝。 –

3

要允許一個變量在塊內改變,使用_ 塊存儲類型修飾符,請參閱「_block Storage Type」。

__block NSString* imString = @"Imutable Hello"; 

參考由apple doc

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

  1. 全局變量是可訪問的,包括en中存在的靜態變量關閉詞彙範圍。
  2. 傳遞給塊的參數是可訪問的(就像函數的參數一樣)。 將封閉詞法作用域局部的堆棧(非靜態)變量作爲常量變量捕獲。
  3. 它們的值取自程序中塊表達式的位置。在嵌套塊中,該值是從最近的封閉範圍捕獲的。

  4. 變量本地與__block存儲修飾符聲明封閉詞法作用域通過引用提供等是可變的。

  5. 任何變化都反映在封閉詞法作用域,包括相同的封閉詞法範圍內所定義的任何其他塊。這些在「__block存儲類型」中有更詳細的討論。

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

  7. 塊的每次調用都會提供該變量的新副本。這些變量可以反過來用作塊內封閉塊的const或by-reference變量。
+0

你忘了提及這是從https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/的逐字拷貝TP40007502-CH6-SW2。 - 解釋這如何適用於特定問題會更有幫助。 –

+0

聲音很好... – codercat

+0

我閱讀了這份文件......但仍然沒有回答我的問題。這些規則中的哪一個使得UIView屬性(不使用__block定義)可以在動畫塊中進行分配。我的原始變量是不是可分配的,UIView的基本屬性是什麼;爲什麼我不能將新對象賦給指針,除非我在聲明中使用__block,並且UIView屬性可以。 –

2

在第一個示例中,塊「捕獲」變量self - 這是一個指向保留對象的指針。你不修改本身在你的例子,當你寫:

self.someProperty = someValue; 

自我的價值仍然是相同的 - 也就是說,它仍指向同一個對象。

你會修改例如如果你寫:

self = nil; 
+0

好的......這個答案實際上導致了某個地方......所以我將一個對象傳遞給該塊的事實使得它的屬性可分配?我通過添加一個int屬性和一個int ivar來測試它 - 我的類沒有__block - ,並且我看到我可以在塊中爲它們分配一個新值。哪個原則/規則可以使屬性/ ivars在塊內分配? Block定義的方法中的局部變量不是? –

+1

@OhadRegev你可以調用一個method_的事實是,接收者的指針(here _self_)不會因爲調用一個方法(實際上是指派一個屬性)而改變。注意_self_是一個指針,它的值可能類似於'self equals 0xabcd1230' _before_調用一個方法,並且在調用方法之後它仍然是相同的:'self等於0xabcd1230'。 – CouchDeveloper

+1

@OhadRegev在處理塊或lambdas時,捕獲的變量(這裏指針_self_的值爲0xabcd1230)是_const_,這只是一個_common約定。它們也可以被修改。 – CouchDeveloper

相關問題