天天看点

iOS底层原理篇(十七) ---- Block初探

1.Block的分类

  • Block有六种:在libclosure的源码中
    //常见的三种
      void * _NSConcreteStackBlock[32] = { 0 };
      void * _NSConcreteMallocBlock[32] = { 0 };
      void * _NSConcreteGlobalBlock[32] = { 0 };
      //系统级别的三种
      void * _NSConcreteAutoBlock[32] = { 0 };
      void * _NSConcreteFinalizingBlock[32] = { 0 };
      void * _NSConcreteWeakBlockVariable[32] = { 0 };
               
常见的三种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的神秘面纱…