1.Block的分類
常見的三種block
- 1.全局block : NSGlobalBlock
- (void)viewDidLoad {
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"我是全局block");
};
block();
NSLog(@"%@",block);
}
//列印結果
//Block探析[2300:140310] 我是全局block
//Block探析[2300:140310] <__NSGlobalBlock__: 0x10d14e038>
- 2.堆區block : NSMallocBlock
- (void)viewDidLoad {
[super viewDidLoad];
int a = 10;
void (^block)(void) = ^{
NSLog(@"我是堆區block---%d",a);
};
block();
NSLog(@"%@",block);
}
//列印結果:
//Block探析[2416:146513] 我是全局block---10
//Block探析[2416:146513] <__NSMallocBlock__: 0x600003db08d0>
- 3.棧區block : NSStackBlock (block在被copy之前就是棧區block)
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@",^{
NSLog(@"我是棧區block");
});
}
//列印結果:
//Block探析[2460:149166] <__NSGlobalBlock__: 0x109b7b038>
2.Block捕獲外部變量
1.無__block修飾的局部變量
#include <stdio.h>
int main() {
int a = 10;
void (^block)(void) = ^{
printf("我是block---%d",a);
};
block();
return 0;
}
//clang生成.cpp檔案
//下面是cpp檔案中main函數的實作代碼
int main() {
int a = 10;
/**
//我們上下代碼對比一下
//下面這句代碼是block的聲明
//傳入了三個參數
//第一個__main_block_func_0 block的代碼塊
//第二個__main_block_desc_0_DATA
//第三個為a的值
//void (*block)(void) = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a);
//這裡面傳入的a為外部變量的值
//(void *)__main_block_func_0:就是下面的結構
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//這裡把block聲明時内部儲存的a值給了一個臨時變量a
//這個a和我們block通路的外部變量不是同一個值
//這裡做的隻是淺拷貝,拷貝了a的值
//是以我們在内部修改a的值時,外部的a是不會改變的
int a = __cself->a; // bound by copy
printf("我是全局block---%d",a);
}
//這裡将我們block代碼塊裡面的代碼給了函數__main_block_func_0
//__main_block_impl_0:構造函數,傳回值為一個block!
//block的本質:結構體
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//聲明的block内部會生成一個 int a 變量用來儲存外部捕獲的變量值!
int a;
//這裡fp傳入的值就是__main_block_func_0
//desc是__main_block_desc_0_DATA
//int _a 就是 a
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
//此時這裡還是棧區block,後期會研究一下在什麼時候變成了堆區block
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
//将代碼塊指派給了impl的FuncPtr
impl.FuncPtr = fp;//函數式儲存
Desc = desc;
}
};
*/
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
/**
//block結構體中的impl結構
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;//代碼塊的函數式儲存
};
//去掉強轉的代碼,這裡不就是block的調用:
block->FuncPtr(block);
*/
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
2.__block修飾的局部變量
#include <stdio.h>
int main() {
__block int a = 10;
void (^block)(void) = ^{
a++;
printf("我是全局block---%d",a);
};
block();
return 0;
}
//clang生成.cpp檔案
//下面是cpp檔案中main函數的實作代碼
int main() {
//__block int a = 10;的代碼處理
//__Block_byref_a_0類型的a
/**
這個與下面的__Block_byref_a_0 a 初始化對比
struct __Block_byref_a_0 {
void *__isa;//0
__Block_byref_a_0 *__forwarding;//a的位址指針
int __flags;//0
int __size;//空間大小
int a;a的值
};
*/
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
(void*)0,
(__Block_byref_a_0 *)&a,
0,
sizeof(__Block_byref_a_0),
10};
//
/**
這裡block初始化的時候,傳進來的是__Block_byref_a_0結構體類型的指針a;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
//初始化的時候,傳入的就是儲存了a的位址指針及值的一個結構體的指針__Block_byref_a_0 *_a
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
*/
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
//調用
/**
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//深拷貝,位址
//__Block_byref_a_0類型的a中__forwarding指針指向的a值
//a->__forwarding->a的值++
(a->__forwarding->a)++
__Block_byref_a_0 *a = __cself->a;
printf("我是全局block---%d",(a->__forwarding->a));
}
*/
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
3.Block循環引用
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"iOS";
self.block = ^{
NSLog(@"我是---%@",self.name);
};
self.block();
}
//這樣一段代碼,不用說,都知道會發生循環應用
//這是因為self->block->self形成了一個閉環,并且都是強持有!
//是以這裡vc和block都無法釋放
//也就是說,隻要打破這個循環,不會循環應用了!
//我們平時最多的使用是
//定義一個弱引用weakSelf,指針指向self,因為是weak修飾,是以不對引用計數做任何操作
//__weak typeof(self) weakSelf = self;隻需将self.name改為weakSelf.name就可打破循環
//這裡使用了中介者設計模式
//weakSelf(弱引用表)->self->block->weakSelf
//看需求及邏輯需要,隻有這一句代碼有可能會有問題,
//比如把上面代碼改為:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.name = @"iOS";
__weak typeof(self) weakSelf = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"我是---%@",weakSelf.name);
});
};
self.block();
}
- (void)dealloc {
NSLog(@"釋放了");
}
//列印結果就是:
//Block探析[3895:288440] 釋放了
//Block探析[3895:288440] 我是---(null)
//列印的時候,vc已經釋放,屬性name肯定也被釋放了
//如果遇到這種情況,我們還需要做一步操作:
//__strong typeof(weakSelf) strongSelf = weakSelf;
//weakSelf.name改為strongSelf.name
//strongSelf臨時變量出了作用域會被釋放
//strongSelf->weakSelf(弱引用表)->self->block->weakSelf->strongSelf
//延遲釋放vc
//Block探析[3922:291133] 我是---iOS
//Block探析[3922:291133] 釋放了
這裡簡單的介紹一下block,下一篇會從底層出發,揭開block的神秘面紗…