Block的使用 iphone

from: http://www.cocoachina.com/newbie/basic/2012/0514/4247.html


本文主要是闡述一下Block中如何的使用外部變量以及block本身的內存管理。
 
先定義一個block變量,作爲後續的例子中使用:
 
  1. typedef void(^BlockCC)(void); 
  2. BlockCC _block; 
 
1、block中引用外部變量
block中可以直接使用外部的變量,比如
 
  1. int number = 1
  2. _block = ^(){ 
  3.     NSLog(@"number %d", number); 
  4. }; 
 
那麼實際上,在block生成的時候,是會把number當做是常量變量編碼到block當中。可以看到,以下的代碼,block中的number值是不會發生變化的:
 
  1. int number = 1
  2. _block = ^(){ 
  3.     NSLog(@"number %d", number); 
  4. }; 
  5. number = 2
  6. _block(); 
則輸出的值爲 1,而不是2。原因就是如上所說。
 
如果要在block中嘗試改變外部變量的值,則會報錯的。對於這個問題的解決辦法是引入__block標識符。將需要在block內部修改的變量標識爲__block scope。更改後的代碼如下:
 
  1. __block int number = 1
  2. _block = ^(){ 
  3.     number++; 
  4.     NSLog(@"number %d", number); 
  5. }; 
而這個時候,其實block外部的number和block內部的number指向了同一個值,回到剛纔的在外部改變block的例子,它的輸出結果將是2,而不是1。有興趣的可以自己寫一個例子試試。
 
2、block自身的內存管理
block本身是像對象一樣可以retain,和release。但是,block在創建的時候,它的內存是分配在棧(stack)上,而不是在堆(heap)上。他本身的作於域是屬於創建時候的作用域,一旦在創建時候的作用域外面調用block將導致程序崩潰。比如下面的例子。
我在view did load中創建了一個block:
 
  1. - (void)viewDidLoad 
  2.     [superviewDidLoad]; 
  3.   
  4.     int number = 1
  5.     _block = ^(){ 
  6.   
  7.     NSLog(@"number %d", number); 
  8. }; 
並且在一個按鈕的事件中調用了這個block:
 
  1. - (IBAction)testDidClick:(id)sender { 
  2.     _block(); 
此時我按了按鈕之後就會導致程序崩潰,解決這個問題的方法就是在創建完block的時候需要調用copy的方法。copy會把block從棧上移動到堆上,那麼就可以在其他地方使用這個block了~
修改代碼如下:
 
  1. _block = ^(){ 
  2.     NSLog(@"number %d", number); 
  3. }; 
  4.   
  5. _block = [_blockcopy]; 
同理,特別需要注意的地方就是在把block放到集合類當中去的時候,如果直接把生成的block放入到集合類中,是無法在其他地方使用block,必須要對block進行copy。不過代碼看上去相對奇怪一些:
 
  1. [array addObject:[[^{ 
  2.     NSLog(@"hello!"); 
  3. } copy] autorelease]]; 

3、循環引用
這一點其實是在第一點的一個小的衍生。當在block內部使用成員變量的時候,比如
 
  1. @interface ViewController : UIViewController 
  2.     NSString *_string; 
  3. @end 
在block創建中:
 
  1. _block = ^(){ 
  2.     NSLog(@"string %@", _string); 
  3. }; 
這裏的_string相當於是self->_string;那麼block是會對內部的對象進行一次retain。也就是說,self會被retain一次。當self釋放的時候,需要block釋放後纔會對self進行釋放,但是block的釋放又需要等self的dealloc中才會釋放。如此一來變形成了循環引用,導致內存泄露。
 
修改方案是新建一個__block scope的局部變量,並把self賦值給它,而在block內部則使用這個局部變量來進行取值。因爲__block標記的變量是不會被自動retain的。
 
  1. __block ViewController *controller = self
  2. _block = ^(){ 
  3.     NSLog(@"string %@", controller->_string); 
  4. }; 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章