一、三种block
1、全局block
- (void)viewDidLoad
{
[super viewDidLoad];
void (^block)(void) = ^{
NSLog(@"hehe");
};
NSLog(@"%@",block); // 打印block对象
}
--------------------
打印:
<__NSGlobalBlock__:0x105f14088> //说明这是一个全局block
2、堆block
- (void)viewDidLoad
{
[super viewDidLoad];
int a = 10;
void (^block)(void) = ^{
NSLog(@"hehe - %d",a);
};
NSLog(@"%@",block); // 打印block对象
}
--------------------
打印:
<__NSMallocBlock__:0x600002cceeb0> //说明这是一个堆block
3、栈block
- (void)viewDidLoad
{
[super viewDidLoad];
int a = 10;
NSLog(@"%@",^{
NSLog(@"hehe - %d",a);
}); // 打印block对象
}
--------------------
打印:
<__NSStackBlock__:0x7ffee09cf800> //说明这是一个堆block
原因:代码块在做=的赋值操作的时候隐藏了一个copy,从栈区拷贝到的堆区
栈:0x7
堆:0x6
静态变量:0x1
二、block的循环引用
1、引用计数与对象的释放
- 当对象A引用对象B时,B的rentainCount +1
- 当A进行dealloc时,向B发送release信号,B的retainCount -1
- 当B的retainCount == 0时,B就会调用dealloc
2、循环引用
A引用了B,此时B的rentainCount==1,然后B引用了A,此时A的rentainCount==(n+1),所以A和B都无法调用dealloc
typedef void(^HHBlock)(void);
@interface ViewController ()
@property (nonatomic , copy) HHBlock block;
@property (nonatomic , copy) NSString *name;
@end
@implementation ViewController
// 循环引用版
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
self.block = ^{
self.name = @"haha";
};
/*
此时,self 持有block,同时,block持有self
*/
self.block;
}
// 升级版
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
__weak typeof(self) weakSelf = self;
// 此处开始self引用计数不会因为block持有而增加
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
// 此处临时变量会在autoreleasepool中自动释放
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
strongSelf.name = @"haha";
});
};
self.block;;
}
// 升级版 2
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
__block ViewController *vc = self;
// __block可以copy一份当前的对象到新建的struct里面
// 目前self持有block持有vc持有self
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
vc.name = @"haha";
vc = nil; //直接给变量释放,不置为nil还是循环引用不能自动释放
//目前self持有block持有nil不再持有self
});
};
self.block;;
}
- (void)dealloc
{
NSLog(@"调用了dealloc");
}
// 骚骚版 3
typedef void(^HHBlock)(ViewController *);
@interface ViewController ()
@property (nonatomic , copy) HHBlock block;
@property (nonatomic , copy) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.name = @"hehe";
self.block = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
vc.name = @"haha";
});
};
self.block(self);
}
- (void)dealloc
{
NSLog(@"调用了dealloc");
}
三、bolck自动捕获变量的特性
1、引出问题
- 对象的底层:struct
- block是个对象
- block捕获变量 -->即--> struct内部增加属性
NSLog的本质是什么?
答:是一套封装了print的C语言函数,比较耗时
- (void)viewDidLoad
{
__block int a = 10;
NSLog(@"进去之前:%p",&a); // 0x7ffee3deb988 - 栈
viod(^block)(void) =^{
// 捕获
a++;
NSLog(@"在里面:%p",&a); // 0x600002647c78 - 捕获后 到 堆区
};
NSLog(@"写完block:%p",&a); // 0x600002647c78 - 看写代码的顺序,不要看调用顺序
block();
}
2、源码初探
// C语言
#include "stdio.h"
int main()
{
void(^block)(void) = ^{
printf("hehe");
};
block;
return 0;
}
---------------------- 执行clang看其内部实现
clang -rewrite-objc testBlcok.c -o blockCPP.cpp
---------------------- 结果很乱但是不用看,看下面简化版的就行
int main()
{
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
---------------------- 简化版
int main()
{
void(*block)(void) = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA); // f(a,b)
block->FuncPtr(block); // 其实就是 block();
return 0;
}
// __main_block_impl_0
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
{
// 初始化
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
// 保存代码块的能力
impl.FuncPtr = fp; // 属性函数 - 保存函数 - __main_block_func_0 其实就是^{ ··· }代码块
Desc = desc;
}
};
3、捕获变量的代码
-
不加__block的int
#include "stdio.h" int main() { int a = 10; void(^blockNN)(void) = ^{ printf("--- %d",a); }; blockNN(); return 0; } ------------ int main() { int a = 10; void(*blockNN)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a)); ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN); return 0; } ------------ int main() { int a = 10; void(*blockNN)(void) = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA, a)); // f(a,b,c) (blockNN->FuncPtr)(blockNN); return 0; }struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int a; // 自动捕获到了变量 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };static void __main_block_func_0(struct __main_block_impl_0 *__cself) { // 创建新的变量 // __cself就是(blockNN->FuncPtr)(blockNN)中的blockNN int a = __cself->a; // bound by copy printf("--- %d",a); } -
加__block的int
#include "stdio.h" int main() { __block int a = 10; void(^blockNN)(void) = ^{ printf("--- %d",a++); }; blockNN(); return 0; } ---------------- int main() { __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10}; void(*blockNN)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)); ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN); return 0; } ----------------- int main() { // 生成了一个含有五个属性的结构体 __Block_byref_a_0 a = { 0, &a, // 引用原来的a的地址 0, sizeof(__Block_byref_a_0), 10 // 原来的a的值 }; void(*blockNN)(void) = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA, &a, 570425344)); // 其中的&a就是上面结构体的指针地址 ((void (*)(__block_impl *))((__block_impl *)blockNN)->FuncPtr)((__block_impl *)blockNN); return 0; }struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref // 自动捕获__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; } };static void __main_block_func_0(struct __main_block_impl_0 *__cself) { // 指针传递 __Block_byref_a_0 *a = __cself->a; // bound by ref // 此*a就是原来的结构体__Block_byref_a_0 a,包含了原来a的地址和值 printf("--- %d",(a->__forwarding->a)++); } -
总结
没有
__block修饰的int a只是传进来一个值,没有原来的a的地址,不能修改,是只读状态有
__block修饰的int a就是指针传递,可读可写