iOS Block的三種類型 globalBlock、stackBlock 、mallocBlock 區(qū)別


在 ARC 中,捕獲了外部變量的 block 的類會是 __NSMallocBlock__ 或者 __NSStackBlock__,如果 block 被賦值給了某個變量,在這個過程中會執(zhí)行 __Block__copy 將原有的 __NSStakeBlock__ 變成 __NSMallocBlock__ ;但是如果 block沒有賦值給某個變量,那他的類型就是 __NSStakeBlock__ ;沒有捕獲外部變量的 block 的類會是 __NSGlobalBlock__ 即不再對上,也不在棧上,它類似 C 語言函數(shù)一樣會在代碼段中。

在非 ARC 中,捕獲了外部變量的 block 的類會是 __NSStackBlock__ ,放在棧上,沒有捕獲外部變量的 block 時與 ARC 環(huán)境下情況相同。

block 中的 isa 指向的是該 block 的 Class 。在 block runtime 中,第一了6種類型:

_NSConcreteStackBlock ? ? 棧上創(chuàng)建的block

_NSConcreteMallocBlock ?堆上創(chuàng)建的block

_NSConcreteGlobalBlock ? 作為全局變量的block

_NSConcreteWeakBlockVariable

_NSConcreteAutoBlock

_NSConcreteFinalizingBlock

我們能接觸到的主要是前三種,后三種用于 GC 不再討論...

當 struct 第一次被創(chuàng)建時,它是存在于該函數(shù)的棧幀上的,其 Class 是固定的 __NSStackBlock 。其捕獲的變量是會賦值到結構體的成員上,所以當 block 初始完成后,捕獲到的變量不能更改。?

當函數(shù)返回時,函數(shù)的棧幀被銷毀,這個 block 的內存也會被清除。所以在函數(shù)結束后,如果仍需要這個 block ,就需要用到 Block_copy() 方法將它拷貝到堆上。這個方法的核心動作是:申請內存,將棧數(shù)據復制過去,將 Class 改一下,最后向捕獲到的對象發(fā)送 retain ,增加 block 的引用計數(shù)。

1.全局 block?

定義在函數(shù)外部的 block 是 global 類型的

定義在函數(shù)內部的 block ,但是沒有捕獲任何自動變量,那也是全局的。

全局靜態(tài) block,不會訪問任何外部變量,執(zhí)行完就銷毀。

typedef

int(^blk_t)(int);

for(...){

blk_t blk = ^(intcount) {returncount;};

}或

^{ NSLog(@"Hello World!"); }();

2.棧 block

保存在棧中的 block,當函數(shù)返回時會被銷毀,和第一種的區(qū)別就是調用了外部變量。

typedef void (^block_t)() ;

-(block_t)returnBlock{

? ? __block int add=10;

? ? return ^{printf("add=%d\n",++add);};

}

這是因為:block捕獲了棧上的add自動變量,此時add已經變成了一個結構體,而block中擁有這個結構體的指針。即如果返回block的話就是返回局部變量的指針。而這一點恰是編譯器已經斷定了。在ARC下可以編譯過,是因為ARC使用了autorelease了。

再說一個場景:

- (block_t) returnBlock{

? ? __block int add=10;

? ? block_t blk_h =^{printf("add=%d\n",++add);};

? ? return blk_h;

}

block_t bb = [self returnBlock];

bb();

這段代碼,只是使用了一個自動block變量,可以編過,但是造成程序崩潰了。

如果在返回block的時候加上copy,可以輸出正確的數(shù)值11

[UIView animateWithDuration:3 animations:^{ self.view.backgroundColor = [UIColor redColor]; }];

3.堆內存

保存在堆中的 block,當引用計數(shù)為 0 時會被銷毀。例如按鈕的點擊事件,一直存在,即使執(zhí)行過,也不銷毀,因為按鈕還可能被點擊。直到持有按鈕的View被銷毀,它才會被銷毀。

有時候我們需要調用 block 的 copy 函數(shù),將 block 拷貝到堆上。如下代碼:

- (id) getBlockArray{

? ? int val =10;

? ? return [NSArray arrayWithObjects:

? ? ? ? ^{NSLog(@"blk0:%d",val);},

? ? ? ? ^{NSLog(@"blk1:%d",val);},nil];

}

id obj = getBlockArray();

typedef void (^blk_t)(void);

blk_t blk = (blk_t){obj objectAtIndex:0};

blk();

這段代碼在最后一行 blk() 會異常,因為數(shù)組中的block是棧上的。因為val是棧上的。解決辦法就是調用copy方法。

這種場景,ARC 也不會為你添加 copy,因為ARC不確定,采取了保守的措施:不添加copy。所以ARC下也是會異常退出。

Block 在棧和堆中的區(qū)別

1.默認情況下,block 默認存儲在棧中;如果將 block 進行一次 copy 操作,block 會轉移到堆中。

2.如果 block 在棧中,block 訪問了外界變量,那么不會對外界進行 retain 操作。

3.在 property 中使用 block 時,一般都是用 copy 修飾 block ,將 block 拷貝到堆上,減少無意中的堆 block 釋放。

Copy 的作用

1. 防止外界修改內部數(shù)據

2.可以使用 copy 保存 block ,這樣可以保住 block 中使用外界對象的生命。

Block的聲明語法

一、block的聲明

int multiplier = 10;

int (^MyBlock) (int) = ^(int num) { return num*multiplier };

int Myblock 是塊對象,返回整形值

"^" 符號將 MyBlock 聲明為一個塊對象

(int) 它有一個參數(shù),參數(shù)的類型也是整形

^(int num) 參數(shù)的名稱是num

^(int num) { return num*multiplier } 這是定義塊對象的語法結構,這部分就是賦給MyBlock變量的值。

{ return num*multiplier?} 這是塊對象的主體部分

1.寫在方法里作為局部變量

returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...};

returnType是返回值

blockName是block名稱

parameterTypes是參數(shù)

//無參數(shù)無返回值

void?(^myblock)() = ^()

{

????NSLog(@"Hello, World!");

};

myblock();


//帶參數(shù)無返回值

void?(^myblock2)(NSString?*string) = ^(NSString?*string){?NSLog(@"%@",string);};

myblock2(@"Hello, World myblock2!");


?//無參數(shù)有返回值

int?(^myblocksss)() = ^(int?i){return?12;};

int?c = myblocksss();

NSLog(@"%i",c);


//有參數(shù)有返回值

int?(^myblock3)(int) = ^(int?i){?return?12 * i; };

int?i = myblock3(3);

NSLog(@"%i",i);


2.作為類的屬性

@property(nonatomic,copy) returnType (^blockName)(parameterTypes)

returnType是返回值

blockName是block名稱

parameterTypes是參數(shù)

3.作為方法參數(shù)

- (void)someMethodThatTakesABlock:(returnType (^)(parameterTypes))blockName;

returnType是返回值

blockName是block名稱

parameterTypes是參數(shù)

4.調用方法是傳入的參數(shù)

[self someMethodThatTakesABlock:^returnType (parameters) {...}];

returnType是返回值

blockName是block名稱

parameterTypes是參數(shù)

5.自定義Block類型

typedef returnType (^TypeName)(parameterTypes);

TypeName blockName = ^returnType(parameters) {...};

returnType是返回值

blockName是block名稱

parameterTypes是參數(shù)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Block基礎回顧 1.什么是Block? 帶有局部變量的匿名函數(shù)(名字不重要,知道怎么用就行),差不多就與C語言...
    Bugfix閱讀 6,903評論 5 61
  • iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實現(xiàn)匿名函數(shù)的特性,B...
    smile刺客閱讀 2,466評論 2 26
  • block.png iOS代碼塊Block 概述 代碼塊Block是蘋果在iOS4開始引入的對C語言的擴展,用來實...
    全棧農民工閱讀 638評論 0 1
  • 思忖著想了好久的故事和邏輯, 不抵念及你時的心率歡喜。 頓口結舌的情話出口成了 木詞訥語的麻瓜一樣 你說傻逼啊, ...
    良乆囍閱讀 366評論 0 5
  • 曾在寺中偶見佛祖真身,佛祖聞我有緣,許我兩世為人看破因果紅塵。 第一世。 我出生在一座小鎮(zhèn)富豪家中,自幼被迫苦讀文...
    酸酸的煤球閱讀 1,291評論 1 5

友情鏈接更多精彩內容