Block原理、变量截获、三种形式、Block中weak/strong self的用法

前言

block虽然经常使用,但是对于其中的原理和一些特性不是很明白,查找了一些资料,算是一次深入的了解和学习,结合自己的理解和相关资料,写一篇总结,以便以后温故,错误或不足的地方希望大家多多指正,一起讨论学习。

1、Block的原理

网上资料解释:
1、Block是将函数及其执行上下文封装起来的对象。
2、带有自动变量(局部变量)的匿名函数。它是C语言的扩充功能。之所以是拓展,是因为C语言不允许存在这样匿名函数。
3、block本质上也是一个oc对象或者说是一个结构体,内部也有一个isa指针。block是封装了函数调用(函数指针)以及函数调用环境(捕获到的参数)的OC对象。

自己的理解:Block是一个封装了变量和方法的对象,可以将其与类进行类比,变量相当于属性,方法相当于类里面的方法或者函数。一般使用较多的是当做属性使用,可以理解为是一个类对象。

2、变量截获

局部变量
静态变量
静态全局变量
全局变量

1、局部变量截获是截获

NSInteger num = 3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
    return n*num;
};
num = 1;
NSLog(@"%zd",block(2));

自己尝试打印下结果,同时尝试下在block内部修改值看看结果。

局部对象变量也是一样,截获的是

NSMutableArray * arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
void(^block)(void) = ^{
    NSLog(@"%@",arr);//局部变量
    [arr addObject:@"4"];
};
[arr addObject:@"3"];
arr = nil;
block();

2、局部静态变量截获是指针截获。

static NSInteger num = 3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
    return n*num;
};
num = 1;
NSLog(@"%zd",block(2));

3、全局变量,静态全局变量截获:不截获,直接取值

//静态全局变量
static NSInteger num3 = 300;
//全局变量
NSInteger num4 = 3000;

//局部变量
NSInteger num1 = 30;
//静态局部变量
static NSInteger num2 = 3;
    void(^block3)(void) = ^{
        NSLog(@"%zd",num1);//局部变量
        NSLog(@"%zd",num2);//局部静态变量
        NSLog(@"%zd",num3);//全局静态变量
        NSLog(@"%zd",num4);//全局变量
    };
    block3();

4、__block修饰变量截获是指针截获。

    __block NSInteger num5 = 30000;
    void(^block3)(void) = ^{
        NSLog(@"%zd",num5);//__block修饰变量
    };
    num5 = 2800;
    block3();

3、Block的3种形式

分为全局Block(_NSConcreteGlobalBlock)、栈Block(_NSConcreteStackBlock)、堆Block(_NSConcreteMallocBlock)三种形式

1、不使用外部变量的block是全局block

NSLog(@"%@",[^{
    NSLog(@"globalBlock");
} class]);

2、使用外部变量并且未进行copy操作的block是栈block,日常开发常用于这种情况

NSInteger num = 10;
NSLog(@"%@",[^{
NSLog(@"stackBlock:%zd",num);
} class]);

3、对栈block进行copy操作,就是堆block,而对全局block进行copy,仍是全局block

比如堆1中的全局进行copy操作,即赋值:

void (^globalBlock)(void) = ^{
    NSLog(@"globalBlock");
    };
NSLog(@"%@",[globalBlock class]);

输出:NSGlobalBlock,仍是全局block

而对2中的栈block进行赋值操作:

NSInteger num = 10;
void (^mallocBlock)(void) = ^{
    NSLog(@"stackBlock:%zd",num);
};
NSLog(@"%@",[mallocBlock class]);

输出:NSMallocBlock

对栈block copy之后,并不代表着栈block就消失了,左边的mallock是堆block,右边被copy的仍是栈block
比如:

[self testWithBlock:^{
	NSLog(@"%@",self);
}];

(void)testWithBlock:(dispatch_block_t)block{
block();
dispatch_block_t tempBlock = block;
NSLog(@"%@,%@",[block class],[tempBlock class]);
}

输出:NSStackBlock,NSMallocBlock

4、weak/strong self的用法

1、weakSelf ;

场景:self 、person、Pblock(person中的block)
self持有person对象,person对象持有Pblock,Pblock中使用到self,对self进行截获,进而持有self,最终结果是:死循环,造成内存泄漏。

持有
持有
截获并持有
self
person
Pblock

使用__weak __typeof(self)weakSelf = self
弱化self对person的持有。
weakSelf解决循环引用

2、strongSelf ;

block截获的变量在超出其作用域后仍能使用(比如block截获了self,然后block又被传递到其它地方使用,此时self按理已经释放)。这其实是因为系统会自动的根据情况将block从栈拷贝到堆中并强引用它截获的变量(一般我们的block最开始都是在栈中的),我们知道栈的内存是由系统管理的,而堆是由程序猿管理的,所以实现了变量超出作用域仍能使用。如果block没有拷贝到堆上,超出作用域self被释放,如果再次使用可能会导致crash。

__weak __typeof(self)weakSelf = self;
blk_t blk = ^() {
    __strong __typeof(weakSelf)strongSelf = weakSelf;
}

strongSelf是为了保证任何情况下self在超出作用域后仍能够使用

下面列出block能够拷贝的情况:
1、调用block的copy方法
2、block作为返回值
3、block赋值时
4、Cocoa框架中方法名中含有usingBlock的方法
5、GCD中

weakSelf是为了解决循环引用
strongSelf是为了保证任何情况下self在超出作用域后仍能够使用

总结:通过学习对block有了深入的了解,在后面的开发中加以灵活使用,进而不断加深对block的理解和研究。至于特别底层的原理实在难以消化,再接再厉吧。

参考链接:
1、https://www.jianshu.com/p/f539c017b0c8
2、https://www.jianshu.com/p/bcd494ba0e22
3、https://blog.csdn.net/u014600626/article/details/78697535
4、Block中weak/strong self的用法

侵权删除!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值