Block的引用迴圈問題 (ARC & non-ARC),block引用迴圈arc

來源:互聯網
上載者:User

Block的引用迴圈問題 (ARC & non-ARC),block引用迴圈arc

  

Block實現原理

首先探究下Block的實現原理,由於Objective-C是C語言的超集,既然OC中的NSObject對象其實是由C語言的struct+isa指標實現的,那麼Block的內部實現估計也一樣,以下三篇Blog對Block的實現機製做了詳細研究:

  • A look inside blocks: Episode 1
  • A look inside blocks: Episode 2
  • A look inside blocks: Episode 3

雖然實現細節看著頭痛,不過發現Block果然是和OC中的NSObject類似,也是用struct實現出來的東西。這個是LLVM項目compiler-rt分析的block頭文Block_private.h標頭檔中關於Block的struct聲明:

123456789101112131415
struct Block_descriptor {    unsigned long int reserved;    unsigned long int size;    void (*copy)(void *dst, void *src);    void (*dispose)(void *);};struct Block_layout {    void *isa;    int flags;    int reserved;    void (*invoke)(void *, ...);    struct Block_descriptor *descriptor;    /* Imported variables. */};

我們發現Block_layout中也有一個isa指標,像極了NSobject內部實現struct中的isa指標。這裡的isa可能指向三種類型之一的Block:

  • _NSConcreteGlobalBlock:全域類型Block,在編譯器就已經確定,直接放在程式碼片段__TEXT上。直接在NSLog中列印的類型為__NSGlobalBlock__。
  • _NSConcreteStackBlock:位於棧上分配的Block,即__NSStackBlock__。
  • _NSConcreteMallocBlock:位於堆上分配的Block,即__NSMallocBlock__。

為什麼會有這麼多種類呢?首先來看全域類型Block,看例子:

123456789101112
void addBlock(NSMutableArray *array) {  [array addObject:^{    printf("global block\n");  }];} void example() {  NSMutableArray *array = [NSMutableArray array];  addBlock(array);  void (^block)() = [array objectAtIndex:0];  block();}

為什麼addBlock中添加到array中的Block屬於全域Block呢?因為它不需要運行時(Runtime)任何的狀態來改變行為,不需要放在堆上或者棧上,直接編譯後在程式碼片段中即可,就像個c函數一樣。這種類型的Block在ARC和non-ARC情況下沒有差別。

這個Block訪問了範圍外的變數d,在實現上就是這個block會多一個成員變數對應這個d,在賦值block時會將方法exmpale中的d變數值複製到成員變數中,從而實現訪問。

1234567
void example() {  int d = 5;  void (^block)() = ^() {      printf("%d\n", d);  };  block();}

如果要修改d呢?:

123456789
void example() {  int d = 5;  void (^block)() = ^() {      d++;      printf("%d\n", d);  };  block();  printf("%d\n", d);}

由於局部變數d和這個block的實現不在同一範圍,僅僅在調用過程中用到了值傳遞,所以不能直接修改,而需要加一個標識符__block int d = 5;,那麼block就可以實現對這個局部變數的修改了。如果是這種block標識的變數,在Block實現中不再是簡單的一個成員變數,而是對應一個新的結構體表示這個block變數。block的本質是引入了一個新的Block_byref{$var_name}{$index}結構體,被block關鍵字修飾的變數就被放到這個結構體中。另外,block結構體通過引入Block_byref{$var_name}{$index}指標類型的成員,得以間接訪問到Block的外部變數。這樣對Block外的變數訪問從值傳遞轉變為引用,從而有了修改內容的能力。

正常我們使用Block是在棧上產生的,離開了棧範圍便釋放了,如果copy一個Block,那麼會將這個Block copy到堆上分配,這樣就不再受棧的限制,可以隨意使用啦。例如:

1234567891011121314
typedef void (^TestBlock)(); TestBlock getBlock() {  char e = 'E';  void (^returnedBlock)() = ^{    printf("%c\n", e);  };  return returnedBlock;} void example() {  TestBlock block = getBlock();  block();}

函數getBlock中聲明並賦值的returnedBlock,一開始是在棧上分配的,屬於NSStackBlock,如果是non-ARC情況下return這個NSStackBlock,那麼其實已經被銷毀了,在函數中example()使用時就會crash。如果是ARC情況下,getBlock返回的block會自動copy到堆上,那麼block的類型就是NSMallocBlock,可以在example()中繼續使用。要在Non-ARC情況下正常運行,那麼就應該修改為:

1234567
TestBlock getBlock() {  char e = 'E';  void (^returnedBlock)() = ^{    printf("%c\n", e);  };  return [[returnedBlock copy] autorelease];}
Block中的循環參考問題

扯了這麼多,回到Block的循環參考問題,由於我們很多行為會導致Block的copy,而當Block被copy時,會對block中用到的對象產生強引用(ARC下)或者引用計數加一(non-ARC下)。

如果遇到這種情況:

123456789
@property(nonatomic, readwrite, copy) completionBlock completionBlock;//========================================self.completionBlock = ^ {        if (self.success) {            self.success(self.responseData);        }    }};

對象有一個Block屬性,然而這個Block屬性中又引用了對象的其他成員變數,那麼就會對這個變數本身產生強應用,那麼變數本身和他自己的Block屬性就形成了循環參考。在ARC下需要修改成這樣:

123456789
@property(nonatomic, readwrite, copy) completionBlock completionBlock;//========================================__weak typeof(self) weakSelf = self;self.completionBlock = ^ {    if (weakSelf.success) {        weakSelf.success(weakSelf.responseData);    }};

也就是產生一個對自身對象的弱引用,如果是倒黴催的項目還需要支援iOS4.3,就用__unsafe_unretained替代__weak。如果是non-ARC環境下就將__weak替換為__block即可。non-ARC情況下,__block變數的含義是在Block中引入一個新的結構體成員變數指向這個__block變數,那麼__block typeof(self) weakSelf = self;就表示Block別再對self對象retain啦,這就打破了循環參考。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.