麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發(fā)設計 > 正文

Block的引用循環(huán)問題(ARC&non-ARC)

2019-11-14 19:32:15
字體:
來源:轉載
供稿:網(wǎng)友

 

2010年WWDC發(fā)布iOS4時Apple對Objective-C進行了一次重要的升級:支持Block。說到底這東西就是閉包,其他高級語音例如java和C++已有支持,第一次使用Block感覺滿簡單好用的,但是慢慢也遇到很多坑。本文聊聊ARC和non-ARC下Block使用中的引用循環(huán)問題,最近遇到了好幾次這種問題,還是深入記錄下。先來套題目熱熱身,貌似能夠全部答對的人蠻少的

Block實現(xiàn)原理

首先探究下Block的實現(xiàn)原理,由于Objective-C是C語言的超集,既然OC中的NSObject對象其實是由C語言的struct+isa指針實現(xiàn)的,那么Block的內(nèi)部實現(xiàn)估計也一樣,以下三篇Blog對Block的實現(xiàn)機制做了詳細研究:

雖然實現(xiàn)細節(jié)看著頭痛,不過發(fā)現(xiàn)Block果然是和OC中的NSObject類似,也是用struct實現(xiàn)出來的東西。這個是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. */};

我們發(fā)現(xiàn)Block_layout中也有一個isa指針,像極了NSobject內(nèi)部實現(xiàn)struct中的isa指針。這里的isa可能指向三種類型之一的Block:

  • _NSConcreteGlobalBlock:全局類型Block,在編譯器就已經(jīng)確定,直接放在代碼段__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)任何的狀態(tài)來改變行為,不需要放在堆上或者棧上,直接編譯后在代碼段中即可,就像個c函數(shù)一樣。這種類型的Block在ARC和non-ARC情況下沒有差別。

這個Block訪問了作用域外的變量d,在實現(xiàn)上就是這個block會多一個成員變量對應這個d,在賦值block時會將方法exmpale中的d變量值復制到成員變量中,從而實現(xiàn)訪問。

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的實現(xiàn)不在同一作用域,僅僅在調(diào)用過程中用到了值傳遞,所以不能直接修改,而需要加一個標識符__block int d = 5;,那么block就可以實現(xiàn)對這個局部變量的修改了。如果是這種block標識的變量,在Block實現(xiàn)中不再是簡單的一個成員變量,而是對應一個新的結構體表示這個block變量。block的本質(zhì)是引入了一個新的Block_byref{$var_name}{$index}結構體,被block關鍵字修飾的變量就被放到這個結構體中。另外,block結構體通過引入Block_byref{$var_name}{$index}指針類型的成員,得以間接訪問到Block的外部變量。這樣對Block外的變量訪問從值傳遞轉變?yōu)橐茫瑥亩辛诵薷膬?nèi)容的能力。

正常我們使用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();}

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

1234567
TestBlock getBlock() {  char e = 'E';  void (^returnedBlock)() = ^{    printf("%c/n", e);  };  return [[returnedBlock copy] autorelease];}

Block中的循環(huán)引用問題

扯了這么多,回到Block的循環(huán)引用問題,由于我們很多行為會導致Block的copy,而當Block被copy時,會對block中用到的對象產(chǎn)生強引用(ARC下)或者引用計數(shù)加一(non-ARC下)。

如果遇到這種情況:

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

對象有一個Block屬性,然而這個Block屬性中又引用了對象的其他成員變量,那么就會對這個變量本身產(chǎn)生強應用,那么變量本身和他自己的Block屬性就形成了循環(huán)引用。在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環(huán)境下就將__weak替換為__block即可。non-ARC情況下,__block變量的含義是在Block中引入一個新的結構體成員變量指向這個__block變量,那么__block typeof(self) weakSelf = self;就表示Block別再對self對象retain啦,這就打破了循環(huán)引用。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 日韩精品中文字幕在线播放 | 欧美日韩手机在线观看 | 精品一区二区在线观看视频 | 色婷婷久久久 | 久久国产经典 | 免费黄网站在线播放 | 国产成人av在线播放 | 2019中文字幕在线播放 | 91精品国产91热久久久做人人 | 久久免费看毛片 | 中文字幕偷拍 | 国产精选电影免费在线观看 | 亚洲无线看 | 成人福利在线观看 | 亚洲欧美日韩在线 | 特黄一级小说 | 姑娘第5集高清在线观看 | 亚洲天堂ww | 欧美三区在线 | 一级毛片手机在线观看 | 一级国产精品一级国产精品片 | 久久久久免费精品 | 欧美xxxx精品另类 | 色啪综合 | 久久国产精品久久久久久电车 | 日本在线不卡一区二区 | 一区二区三区国产视频 | 国产精品久久久久久久久久大牛 | 久久99国产精品久久 | 羞羞网站在线看 | 毛片视频网址 | 国产精品视频专区 | 国产精品一区二区免费在线观看 | 97久久精品一区二区三区观看 | 爽爽淫人综合网网站 | 久久99精品视频在线观看 | 国产一级在线看 | 欧美精品国产综合久久 | 免费观看黄色一级视频 | 欧美 国产 综合 | 狠狠操视频网站 |