天天看點

正确使用Block避免Cycle Retain和Crash

Block簡介

Block作為C語言的擴充,并不是高新技術,和其他語言的閉包或lambda表達式是一回事。需要注意的是由于Objective-C在iOS中不支援GC機制,使用Block必須自己管理記憶體,而記憶體管理正是使用Block坑最多的地方,錯誤的記憶體管理 要麼導緻return cycle記憶體洩漏要麼記憶體被提前釋放導緻crash。 Block的使用很像函數指針,不過與函數最大的不同是:Block可以通路函數以外、詞法作用域以内的外部變量的值。換句話說,Block不僅 實作函數的功能,還能攜帶函數的執行環境。

可以這樣了解,Block其實包含兩個部分内容

  1. Block執行的代碼,這是在編譯的時候已經生成好的;
  2. 一個包含

    Block執行時需要的所有外部變量值

    的資料結構。 Block将使用到的、作用域附近到的

    變量的值

    建立一份快照拷貝到棧上。

Block與函數另一個不同是,Block類似ObjC的對象,可以使用自動釋放池管理記憶體(但Block并不完全等同于ObjC對象,後面将詳細說明)。

Block基本文法

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">10</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">11</span>
      
// 聲明一個Block變量
long (^sum) (int, int) = nil;
// sum是個Block變量,該Block類型有兩個int型參數,傳回類型是long。

// 定義Block并賦給變量sum
sum = ^ long (int a, int b) {
  return a + b;
};

// 調用Block:
long s = sum(1, 2);

           

定義一個執行個體函數,該函數傳回Block:

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
      
- (long (^)(int, int)) sumBlock {
    int base = 100;
    return [[ ^ long (int a, int b) {
      return base + a + b;
    } copy] autorelease];
  }

// 調用Block
[self sumBlock](1,2);

           

是不是感覺很怪?為了看的舒服,我們把Block類型typedef一下

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
      
typedef long (^BlkSum)(int, int);

- (BlkSum) sumBlock {
    int base = 100;
    BlkSum blk = ^ long (int a, int b) {
      return base + a + b;
    }
    return [[blk copy] autorelease];
}

           

Block在記憶體中的位置

根據Block在記憶體中的位置分為三種類型NSGlobalBlock,NSStackBlock, NSMallocBlock。

  • NSGlobalBlock:類似函數,位于text段;
  • NSStackBlock:位于棧記憶體,函數傳回後Block将無效;
  • NSMallocBlock:位于堆記憶體。
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">10</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">11</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">12</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">13</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">14</span>
      
BlkSum blk1 = ^ long (int a, int b) {
  return a + b;
};
NSLog(@"blk1 = %@", blk1);// blk1 = <__NSGlobalBlock__: 0x47d0>


int base = 100;
BlkSum blk2 = ^ long (int a, int b) {
  return base + a + b;
};
NSLog(@"blk2 = %@", blk2); // blk2 = <__NSStackBlock__: 0xbfffddf8>

BlkSum blk3 = [[blk2 copy] autorelease];
NSLog(@"blk3 = %@", blk3); // blk3 = <__NSMallocBlock__: 0x902fda0>

           

為什麼blk1類型是NSGlobalBlock,而blk2類型是NSStackBlock?blk1和blk2的差別在于,blk1沒有使用Block以外的任何外部變量,Block不需要建立局部變量值的快照,這使blk1與函數沒有任何差別,從blk1所在記憶體位址0x47d0猜測編譯器把blk1放到了text代碼段。blk2與blk1唯一不同是的使用了局部變量base,在定義(注意是定義,不是運作)blk2時,局部變量base目前值被copy到棧上,作為

常量

供Block使用。執行下面代碼,結果是203,而不是204。

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
      
int base = 100;
  base += 100;
  BlkSum sum = ^ long (int a, int b) {
    return base + a + b;
  };
  base++;
  printf("%ld",sum(1,2));

           
在Block内變量base是隻讀的,如果想在Block内改變base的值,在定義base時要用 

__block

修飾:

__block int base = 100;

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
      
__block int base = 100;
  base += 100;
  BlkSum sum = ^ long (int a, int b) {
    base += 10;
    return base + a + b;
  };
  base++;
  printf("%ld\n",sum(1,2));
  printf("%d\n",base);

           
輸出将是214,211。Block中使用

__block

修飾的變量時,将取變量此刻運作時的值,而不是定義時的快照。這個例子中,執行

sum(1,2)

時,base将取

base++

之後的值,也就是201,再執行Block

base+=10; base+a+b

,運作結果是214。執行完Block時,base已經變成211了。

Block的copy、retain、release操作

不同于NSObjec的copy、retain、release操作:

  • Block_copy與copy等效,Block_release與release等效;
  • 對Block不管是retain、copy、release都不會改變引用計數retainCount,retainCount始終是1;
  • NSGlobalBlock:retain、copy、release操作都無效;
  • NSStackBlock:retain、release操作無效,必須注意的是,NSStackBlock在函數傳回後,Block記憶體将被回收。即使retain也沒用。容易犯的錯誤是[

    [mutableAarry addObject:stackBlock]

    ,在函數出棧後,從mutableAarry中取到的stackBlock已經被回收,變成了野指針。正确的做法是先将stackBlock copy到堆上,然後加入數組:

    [mutableAarry addObject:[[stackBlock copy] autorelease]]

    。支援copy,copy之後生成新的NSMallocBlock類型對象。
  • NSMallocBlock支援retain、release,雖然retainCount始終是1,但記憶體管理器中仍然會增加、減少計數。copy之後不會生成新的對象,隻是增加了一次引用,類似retain;
  • 盡量不要對Block使用retain操作。

Block對不同類型的變量的存取

基本類型
  • 局部自動變量,在Block中隻讀。Block定義時copy變量的值,在Block中作為常量使用,是以即使變量的值在Block外改變,也不影響他在Block中的值。
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
      
int base = 100;
BlkSum sum = ^ long (int a, int b) {
  // base++; 編譯錯誤,隻讀
  return base + a + b;
};
base = 0;
printf("%ld\n",sum(1,2)); // 這裡輸出是103,而不是3

           
  • static變量、全局變量。如果把上個例子的base改成全局的、或static。Block就可以對他進行讀寫了。因為全局變量或靜态變量在記憶體中的位址是固定的,Block在讀取該變量值的時候是直接從其所在記憶體讀出,擷取到的是最新值,而不是在定義時copy的常量。
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
      
static int base = 100;
BlkSum sum = ^ long (int a, int b) {
  base++;
  return base + a + b;
};
base = 0;
printf("%d\n", base);
printf("%ld\n",sum(1,2)); // 這裡輸出是3,而不是103
printf("%d\n", base);

           

輸出結果是

0 4 1

,表明Block外部對base的更新會影響Block中的base的取值,同樣Block對base的更新也會影響Block外部的base值。

  • Block變量,被

    __block

    修飾的變量稱作Block變量。 基本類型的Block變量等效于全局變量、或靜态變量。
Block被另一個Block使用時,另一個Block被copy到堆上時,被使用的Block也會被copy。但作為參數的Block是不會發生copy的。
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">10</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">11</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">12</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">13</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">14</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">15</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">16</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">17</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">18</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">19</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">20</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">21</span>
      
void foo() {
  int base = 100;
  BlkSum blk = ^ long (int a, int b) {
    return  base + a + b;
  };
  NSLog(@"%@", blk); // <__NSStackBlock__: 0xbfffdb40>
  bar(blk);
}

void bar(BlkSum sum_blk) {
  NSLog(@"%@",sum_blk); // 與上面一樣,說明作為參數傳遞時,并不會發生copy

  void (^blk) (BlkSum) = ^ (BlkSum sum) {
    NSLog(@"%@",sum);     // 無論blk在堆上還是棧上,作為參數的Block不會發生copy。
    NSLog(@"%@",sum_blk); // 當blk copy到堆上時,sum_blk也被copy了一分到堆上上。
  };
  blk(sum_blk); // blk在棧上

  blk = [[blk copy] autorelease];
  blk(sum_blk); // blk在堆上
}

           
ObjC對象,不同于基本類型,Block會引起對象的引用計數變化。

先看下面代碼

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">10</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">11</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">12</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">13</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">14</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">15</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">16</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">17</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">18</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">19</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">20</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">21</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">22</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">23</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">24</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">25</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">26</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">27</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">28</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">29</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">30</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">31</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">32</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">33</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">34</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">35</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">36</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">37</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">38</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">39</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">40</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">41</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">42</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">43</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">44</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">45</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">46</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">47</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">48</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">49</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">50</span>
      
@interface MyClass : NSObject {
    NSObject* _instanceObj;
}
@end

@implementation MyClass

NSObject* __globalObj = nil;

- (id) init {
    if (self = [super init]) {
        _instanceObj = [[NSObject alloc] init];
    }
    return self;
}

- (void) test {
    static NSObject* __staticObj = nil;
    __globalObj = [[NSObject alloc] init];
    __staticObj = [[NSObject alloc] init];

    NSObject* localObj = [[NSObject alloc] init];
    __block NSObject* blockObj = [[NSObject alloc] init];

    typedef void (^MyBlock)(void) ;
    MyBlock aBlock = ^{
        NSLog(@"%@", __globalObj);
        NSLog(@"%@", __staticObj);
        NSLog(@"%@", _instanceObj);
        NSLog(@"%@", localObj);
        NSLog(@"%@", blockObj);
    };
    aBlock = [[aBlock copy] autorelease];
    aBlock();

    NSLog(@"%d", [__globalObj retainCount]);
    NSLog(@"%d", [__staticObj retainCount]);
    NSLog(@"%d", [_instanceObj retainCount]);
    NSLog(@"%d", [localObj retainCount]);
    NSLog(@"%d", [blockObj retainCount]);
}
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        MyClass* obj = [[[MyClass alloc] init] autorelease];
        [obj test];
        return 0;
    }
}

           

執行結果為

1 1 1 2 1

__globalObj

__staticObj

在記憶體中的位置是确定的,是以Block copy時不會retain對象。

_instanceObj

在Block copy時也沒有直接retain 

_instanceObj

對象本身,但會retain self。是以在Block中可以直接讀寫

_instanceObj

變量。

localObj

在Block copy時,系統自動retain對象,增加其引用計數。

blockObj

在Block copy時也不會retain。

非ObjC對象,如GCD隊列dispatch_queue_t。Block copy時并不會自動增加他的引用計數,這點要非常小心。

Block中使用的ObjC對象的行為

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
@property (nonatomic, copy) void(^myBlock)(void);

MyClass* obj = [[[MyClass alloc] init] autorelease];
self.myBlock = ^ {
  [obj doSomething];
};

           

對象obj在Block被copy到堆上的時候自動retain了一次。因為Block不知道obj什麼時候被釋放,為了不在Block使用obj前被釋放,Block retain了obj一次,在Block被釋放的時候,obj被release一次。

retain cycle

retain cycle問題的根源在于Block和obj可能會互相強引用,互相retain對方,這樣就導緻了retain cycle,最後這個Block和obj就變成了孤島,誰也釋放不了誰。比如:

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
      
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
  NSString* string = [request responseString];
}];

           
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
+-----------+           +-----------+
       | request   |           |   Block   |
  ---> |           | --------> |           |
       | retain 2  | <-------- | retain 1  |
       |           |           |           |
       +-----------+           +-----------+

           

解決這個問題的辦法是使用弱引用打斷retain cycle:

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
      
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
  NSString* string = [request responseString];
}];

           
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
+-----------+           +-----------+
      | request   |           |   Block   |
 ---->|           | --------> |           |
      | retain 1  | < - - - - | retain 1  |
      |           |   weak    |           |
      +-----------+           +-----------+

           

request

被持有者釋放後。request 的retainCount變成0,request被dealloc,request釋放持有的Block,導緻Block的retainCount變成0,也被銷毀。這樣這兩個對象記憶體都被回收。

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
+-----------+           +-----------+
      | request   |           |   Block   |
 --X->|           | ----X---> |           |
      | retain 0  | < - - - - | retain 0  |
      |           |   weak    |           |
      +-----------+           +-----------+

           

與上面情況類似的陷阱:

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
      
self.myBlock = ^ {
  [self doSomething];
};

           

這裡self和myBlock循環引用,解決辦法同上:

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
      
__block MyClass* weakSelf = self;
self.myBlock = ^ {
  [weakSelf doSomething];
};

           
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
      
@property (nonatomic, retain) NSString* someVar;

self.myBlock = ^ {
  NSLog(@"%@", _someVer);
};

           

這裡在Block中雖然沒直接使用self,但使用了成員變量。在Block中使用成員變量,retain的不是這個變量,而會retain self。解決辦法也和上面一樣。

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
@property (nonatomic, retain) NSString* someVar;

__block MyClass* weakSelf = self;
self.myBlock = ^ {
  NSLog(@"%@", self.someVer);
};

           

或者

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
      
NSString* str = _someVer;
self.myBlock = ^ {
  NSLog(@"%@", str);
};

           

retain cycle不隻發生在兩個對象之間,也可能發生在多個對象之間,這樣問題更複雜,更難發現

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
      
ClassA* objA = [[[ClassA alloc] init] autorelease];
  objA.myBlock = ^{
    [self doSomething];
  };
  self.objA = objA;

           
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
      
+-----------+           +-----------+           +-----------+
  |   self    |           |   objA    |           |   Block   |
  |           | --------> |           | --------> |           |
  | retain 1  |           | retain 1  |           | retain 1  |
  |           |           |           |           |           |
  +-----------+           +-----------+           +-----------+
       ^                                                |
       |                                                |
       +------------------------------------------------+

           

解決辦法同樣是用

__block

打破循環引用

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
      
ClassA* objA = [[[ClassA alloc] init] autorelease];

MyClass* weakSelf = self;
objA.myBlock = ^{
  [weakSelf doSomething];
};
self.objA = objA;

           
注意:MRC中

__block

是不會引起retain;但在ARC中

__block

則會引起retain。ARC中應該使用

__weak

__unsafe_unretained

弱引用。

__weak

隻能在iOS5以後使用。

Block使用對象被提前釋放

看下面例子,有這種情況,如果不隻是

request

持有了Block,另一個對象也持有了Block。

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
+-----------+           +-----------+
      | request   |           |   Block   |   objA
 ---->|           | --------> |           |<--------
      | retain 1  | < - - - - | retain 2  |
      |           |   weak    |           |
      +-----------+           +-----------+

           

這時如果request 被持有者釋放。

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
      
+-----------+           +-----------+
      | request   |           |   Block   |   objA
 --X->|           | --------> |           |<--------
      | retain 0  | < - - - - | retain 1  |
      |           |   weak    |           |
      +-----------+           +-----------+

           

這時request已被完全釋放,但Block仍被objA持有,沒有釋放,如果這時觸發了Block,在Block中将通路已經銷毀的request,這将導緻程式crash。為了避免這種情況,開發者必須要注意對象和Block的生命周期。

另一個常見錯誤使用是,開發者擔心retain cycle錯誤的使用

__block

。比如

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
      
__block kkProducView* weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
  weakSelf.xx = xx;
});

           

将Block作為參數傳給dispatch_async時,系統會将Block拷貝到堆上,如果Block中使用了執行個體變量,還将retain self,因為dispatch_async并不知道self會在什麼時候被釋放,為了確定系統排程執行Block中的任務時self沒有被意外釋放掉,dispatch_async必須自己retain一次self,任務完成後再release self。但這裡使用

__block

,使dispatch_async沒有增加self的引用計數,這使得在系統在排程執行Block之前,self可能已被銷毀,但系統并不知道這個情況,導緻Block被排程執行時self已經被釋放導緻crash。

<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">1</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">2</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">3</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">4</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">5</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">6</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">7</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">8</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">9</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">10</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">11</span>
<span class="line-number" style="margin: 0px; padding: 0px; border: 0px; font-family: inherit; font-style: inherit; font-variant: inherit; line-height: inherit; vertical-align: baseline; color: rgb(88, 110, 117) !important;">12</span>
      
// MyClass.m
- (void) test {
  __block MyClass* weakSelf = self;
  double delayInSeconds = 10.0;
  dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
  dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    NSLog(@"%@", weakSelf);
});

// other.m
MyClass* obj = [[[MyClass alloc] init] autorelease];
[obj test];

           

這裡用dispatch_after模拟了一個異步任務,10秒後執行Block。但執行Block的時候

MyClass* obj

已經被釋放了,導緻crash。解決辦法是不要使用

__block

繼續閱讀