iOS:block的底层原理

大家好,我是OB!今天来聊聊大家的老熟人Block!

block

block 本质就是NSObject对象,把方法包装成了block块

来看看block的真面目

        
void(^OBblock)(void) = ^{
	NSLog(@"-------");
};
OBblock();

/*
* 编译后
*/
void(*OBblock)(void) = &__main_block_impl_0( 
 											__main_block_func_0,
  											&__main_block_desc_0_DATA 
 										 );
        
        
OBblock->FuncPtr(OBblock);

编译后


struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
        
//block 最终变成了 __main_block_impl_0类型的结构体   
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  //构造函数,返回 __main_block_impl_0 类型的实例
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//block块中的实现,放在这个函数中
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

	NSLog((NSString *)&__NSConstantStringImpl__var_fo_T_main_0072ab_mi_0);
}

        

值捕获

//全局变量
int height_ = 20;
static int weight_  = 20;

void test() {  
	auto int num = 20;
	static int age = 20;
	void(^OBblock)(void) = ^{
    	NSLog(@"num:%d age:%d %d %d",num,age,height_,weight_);
	};
	num = 10;
	age = 10;
	OBblock();
}

再看看结构体发生变化没


void(*OBblock)(void) = &__main_block_impl_0(
__main_block_func_0,
 &__main_block_desc_0_DATA, 
 num, //值捕获,20已经被赋值到结构体中了
 &age //指针捕获
 );

//全局变量height_,weight_没有捕获
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int num;
  int *age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _num, int *_age, int flags=0) : num(_num), age(_age) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

多了num *age构造函数也多了一个: num(_num) age(_age)(将_nun的值自动赋值给num变量),但是 全局变量height_,weight_没有捕获却没有捕获全局变量,

调用函数实现:num直接取值__cself->num,static变量:__cself->age指针传递,获取age地址取值,全局变量height_,weight_直接访问

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int num = __cself->num; // bound by copy
  int *age = __cself->age; // bound by copy

    NSLog((NSString *)&__NSConstantStringImpl__..._mi_0,num,(*age),height_,weight_);
}

捕获与否取决去block的函数执行块,执行时,能不能访问改变量。

变量类型block捕获访问方式能否修改
局部变量:auto捕获值传递添加__block 修饰
局部变量:static捕获指针传递可直接修改
全局变量:static/auto不捕获直接访问可直接修改
block类型

block 本质是一个结构体,换成OC那就是block本质是NSObject;

void getType(id obj) {
    if (obj == NULL) { return; }
    Class currentClass = [obj class];
    NSLog(@"%@",currentClass);
    getType([(NSObject*)currentClass superclass]);
}

可以看到结果:block 也是继承 NSObject

Test_05_block[56306:5305484] __NSGlobalBlock__
Test_05_block[56306:5305484] __NSGlobalBlock
Test_05_block[56306:5305484] NSBlock
Test_05_block[56306:5305484] NSObject

进一步探索发现:block的三种类型

block访问的变量类型block类型copy操作
没有访问局部auto变量__NSGlobalBlock__一直在数据区什么也不做,反正也没有访问变量,一般不考虑
访问局部auto变量__NSStackBlock__从栈区copy到了堆区,StackBlock变成了MallocBlock
NSStackBlock调用了copy__NSMallocBlock__引用计数加一

注意:NSStackBlock调用了copy变成__NSMallocBlock__其实就是对象的生命周期或者是变量的生命周期不统一,需要放到一个能长久访问的地方,防止程序访问时访问不到值。所以ARC才会自动给NSStackBlock copy一下,变成__NSMallocBlock__

循环引用

使用block会造成block捕获对象,会造成对象的引用计数+1,这时就会可能造成循环引用

解决循环引用方法操作安全性
__weak指针指向的对象销毁时,自动置为nil安全
__unsafe_unretained指针指向的对象销毁时,还指向那个地址不安全
__block利用在block中可以修改对象的特性,将obj=nil安全

不使用 __weak


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Animal *animal; //对象引用计数+1
...
};

使用 __weak


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  Animal *__weak animal; //变成弱引用
...
};

block和delegate相比

优点
  1. 回调的block代码块定义在委托对象函数内部,使代码更为紧凑,使用方便
  2. 被委托对象不再需要实现具体某个protocol,代码更为简洁
缺点
  1. delegate运行成本低,block成本很高。
    block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除;delegate只是保存了一个对象指针,直接回调,没有额外消耗
  2. 如果在block里面使用了self,容易导致循环引用问题,要用weak
  3. delegate 更适用于多个回调方法,block 则适用于少量回调
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值