2012-11-01 57 views
3

最近我瞭解到如果我需要保留一個塊對象,我應該複製該塊,因爲它是在堆棧上創建的。iOS塊對象沒有複製?

然後我回顧一下我的代碼。我發現我做的是這樣的:

@implementation MyView 
... 
-(void) addButton:(NSString *)title clickAction:(void ^(void)) action { 
    [self.buttons addObject:@[ title, action ] ]; 
} 
-(void) createButtons { ... } 
... 
@end 

... 

// Somewhere in my project: 
-(void) init { 
    MyView *view = [MyView new]; 
    [view addButton:@"OK"  clickAction:^{ NSLog(@"OK");  }]; 
    [view addButton:@"Cancel" clickAction:^{ NSLog(@"Cancel"); }]; 
} 

基本上,我添加一些按鈕的信息給MyView。在某個時候,MyView會創建按鈕。當按鈕被點擊時,MyView會找出相應的clickAction,並調用它。

奇怪的是,這個程序工作正常,沒有例外,沒有錯誤。

那麼,爲什麼程序運行時沒有複製塊對象呢?

其他信息: * iPhone應用程序(4.3 - 6.0) *非ARC *的XCode 4.5

回答

7

幕後,模塊實現由兩個部分組成:

  • 的可執行代碼塊,和
  • 包含在塊中使用的變量的值的數據結構,

只有塊的第二部分放在堆棧上並複製到堆內存中;第一部分被編譯到您的程序的代碼段,並且不會被複制。

由於您的塊實現不引用周圍範圍內的任何變量,因此它們塊的數據部分爲空。這就是爲什麼你的代碼有效:沒有什麼可以複製的!但是,無論如何,您應該添加塊複製,因爲否則對塊的代碼進行小的更改將會破壞您的應用程序,而編譯器不會注意到任何內容。

+0

我敢肯定,無論塊有多複雜,代碼仍然可以工作,因爲'action'被放置在'NSArray'中,這會導致它被集合保留... –

+0

@JodyHagins Retaining不是問題在於,堆棧上分配的主體超出了範圍,會導致問題。當該方法存在時,其內部創建的塊將最終存儲在內存的無效區域中。這就是爲什麼你需要將它複製到堆上。 – dasblinkenlight

+0

正確 - 但當NSArray保留它時,它會被複制到堆中。從此,NSArray中的對象將成爲堆版本,而不是原始堆棧塊。如果您在基於堆棧的塊上調用retain,它會將其複製到堆中,然後保留它。 –

0

編譯器根據塊的作用創建不同類型的塊。如果一個塊沒有捕獲任何變量,那麼它可以用一個NSGlobalBlockNSGlobalBlock僅僅是一個函數指針)來表示。我懷疑這就是發生了什麼事。

+0

謝謝,好像你和@dasblinkenlight有相同的意見。我會接受dasblinkenlight的答案,因爲他的答案更詳細。 – MorrisLiang