Block(二) 的內存管理以及特性
一 . Block放在哪里
Block 分為三種類型:
- _NSConcreteGlobalBlock (全局block)
- _NSConcreteStackBlock (棧block)
- _NSConcreteMallocBlock (堆block)
這張圖里面很好的解釋了三個block在內存里面的分布

alt text
_NSConcreteGlobalBlock
1. 當block 定義在全局作用域的時候,就是global_block
2. 當block 的方法不獲取任何外部變量的時候,就是global_block
除此之外,其他形式的block都是stack_block,當然也有例外。
在這里還是用代碼的形式來學習比較合適。
棧和堆
void foo()
{
__block int i = 1024;
int j = 1;
void (^blk)(void);
void (^blkInHeap)(void);
blk = ^{
printf("%d, %d\n", i, j);
};
blkInHeap = CFBridgingRelease((__bridge CFTypeRef _Nullable)(blk));
}
- (void)fooBar
{
void (^blk)(BOOL);
BlockTest* t = [[BlockTest alloc]init];
t.block = blk;
void (^oblk)(void) = ^{ printf("%d\n", t.block);};
void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中
}
對于堆跟棧block的變量,我們可以看下面這個例子。
- (void)tt{
__block int i = 1024;
int j = 1;
void (^block)(void);
block = ^{
NSLog(@"%d,%d",i,j);
};
block();
void(^blockInHeap)(void) = [block copy];
blockInHeap();
i++;
j++;
block();
blockInHeap();
}
我在這里解釋一下這段代碼過程中發生的事情。
在我們定義一個__block int i;之后i就已經在棧上了,之后我們定義j,j還不在棧上。
在我們block = ^{} 的時候他會拷貝沒有__block標記變量的內存區,這時候j就在棧上了。
void(^blockInHeap)(void) = [block copy];的時候會拷貝block的內存去到堆上。
在i++ j++之后,由于之前就已經拷貝了一份j的復制品,這時候展示出來的j是在棧上的j,而不是真正的j。而i不同,因為i被記錄下來的是地址。
全局block
static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};
對于全局block里面的變量與block的存儲沒什么關系,反正是全局變量,大家都訪問得到。
二 . 其他特性
對block進行復制:
- 對全局的block 調用copy,會返回原指針。(這個很好理解)
- 對于棧上block 調用copy,會返回新復制到堆上的block的指針,而且__block變量也會被多拷貝一份。
- 對于在heap上的block,調用copy,只會增加他的飲用計數