天天看點

Block使用中的一些疑問解答

本文主要是闡述一下Block中如何的使用外部變量以及block本身的記憶體管理。

先定義一個block變量,作為後續的例子中使用:

  1. typedefvoid(^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. number =2;
  2. _block();

則輸出的值為 1,而不是2。原因就是如上所說。

如果要在block中嘗試改變外部變量的值,則會報錯的。對于這個問題的解決辦法是引入__block辨別符。将需要在block内部修改的變量辨別為__block scope。更改後的代碼如下:

  1. __block int number =1;
  2. number++;

而這個時候,其實block外部的number和block内部的number指向了同一個值,回到剛才的在外部改變block的例子,它的輸出結果将是2,而不是1。有興趣的可以自己寫一個例子試試。

2、block自身的記憶體管理

block本身是像對象一樣可以retain,和release。但是,block在建立的時候,它的記憶體是配置設定在棧(stack)上,而不是在堆(heap)上。他本身的作于域是屬于建立時候的作用域,一旦在建立時候的作用域外面調用block将導緻程式崩潰。比如下面的例子。

我在view did load中建立了一個block:

  1. -(void)viewDidLoad
  2. {
  3. [superviewDidLoad];
  4. }

并且在一個按鈕的事件中調用了這個block:

  1. -(IBAction)testDidClick:(id)sender {

此時我按了按鈕之後就會導緻程式崩潰,解決這個問題的方法就是在建立完block的時候需要調用copy的方法。copy會把block從棧上移動到堆上,那麼就可以在其他地方使用這個block了~

修改代碼如下:

  1. _block =[_blockcopy];

同理,特别需要注意的地方就是在把block放到集合類當中去的時候,如果直接把生成的block放入到集合類中,是無法在其他地方使用block,必須要對block進行copy。不過代碼看上去相對奇怪一些:

  1. [array addObject:[[^{
  2. NSLog(@"hello!");
  3. } copy] autorelease]];

3、循環引用

這一點其實是在第一點的一個小的衍生。當在block内部使用成員變量的時候,比如

  1. @interfaceViewController:UIViewController
  2. NSString*_string;
  3. @end

在block建立中:

  1. NSLog(@"string %@", _string);

這裡的_string相當于是self->_string;那麼block是會對内部的對象進行一次retain。也就是說,self會被retain一次。當self釋放的時候,需要block釋放後才會對self進行釋放,但是block的釋放又需要等self的dealloc中才會釋放。如此一來變形成了循環引用,導緻記憶體洩露。

  1. __block ViewController*controller =self;
  2. NSLog(@"string %@", controller->_string);

繼續閱讀