iOS专攻

你想了解的Block,都告诉你

2019-08-28  本文已影响0人  han_zero

Block

block的本质 -- -- OC对象

block最终都是继承自NSBlock类型,而NSBlock继承于NSObjcet。那么block其中的isa指针其实是来自NSObject中的。这也更加印证了block的本质其实就是OC对象

block的捕获机制

局部变量:
会被block捕获到内部
auto类型的是值传递,内部不能修改值
static是指针传递,可以修改值。

全局变量:
不会被捕获到block内部
可以修改值

Q:为什么局部变量需要捕获?
考虑作用域的问题,需要跨函数访问,就需要捕获
<style>
td
{
text-align:center;
}
</style>

<table border="1" bgcolor="#778899" class="td">
<tr>
<th colspan="2">变量类型</th>
<th>捕获到block内部</th>
<th>访问方式</th>
</tr>
<tr>
<td rowspan="2">局部变量</td>
<td>auto</td>
<td>√️</td>
<td>值传递</td>
</tr>
<tr>
<td>static</td>
<td>√</td>
<td>地址传递</td>
</tr>
<tr>
<td colspan="2">全局变量</td>
<td>×</td>
<td>直接访问</td>
</tr>
</table>

(html代码转义失败了,直接看截图吧😅)


image.png

block有哪几种类型

ARC环境下,一旦Block赋值就会触发copy,__block就会copy到堆上,Block也是__NSMallocBlock。ARC环境下也是存在__NSStackBlock的时候,这种情况下,__block就在栈上。
MRC环境下,只有copy,__block才会被复制到堆上,否则,__block一直都在栈上,block也只是__NSStackBlock,这个时候__forwarding指针就只指向自己了。

在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以才会产生循环引用的问题!

<pre>
int age = 1;
void (^block1)(void) = ^{
NSLog(@"block1");
};

void (^block2)(void) = ^{
NSLog(@"block2:%d",age);
};

NSLog(@"%@/%@/%@",[block1 class],[block2 class],[^{
NSLog(@"block3:%d",age);
} class]);
</pre>

堆block

1.如果block被拷贝到堆上:

2.如果block从堆上移除

Q:当block内部访问了对象类型的auto变量时,是否会强引用?

解决堆block的循环引用办法:

masonry解决的办法是释放block

- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
    self.translatesAutoresizingMaskIntoConstraints = NO;
    MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
    block(constraintMaker);
    return [constraintMaker install];
}

看as_makeConstraints的方法实现会发现设置布局的方法中的block对象并没有被View所引用,block(constraintMaker)而是直接在方法内部同步执行,执行完以后block将释放,其中捕捉的外部变量的引用计数也将还原到之前。这个block只是个栈block,栈block有个特性就是它执行完毕之后就出栈,出栈了就会被释放掉,构不成循环引用。

__weak typeof(person) weakPerson = person;
__unsafe_unretained Person *person = [[Person alloc] init];
__block Person *person = [[Person alloc] init];

__weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
__unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变
__block:必须把引用对象置为nil,并且要调用该block

weakSelf 是为了block不持有self,避免Retain Circle循环引用。在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放,就需要加入strongSelf。block执行完后这个strongSelf 会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。
strongSelf实质是一个局部变量(在block这个“函数”里面的局部变量),当block执行完毕就会释放自动变量strongSelf,不会对self进行一直进行强引用。

上一篇 下一篇

猜你喜欢

热点阅读