不少人在面试的时候都会被问到Block,那么Block分为哪几种类型呢? 其实Block共有6种类型,其中三种经常使用级别,分别是:_NSConcreteGlobalBlock
_NSConcreteStackBlock
_NSConcreteMallocBlock
,三种系统级别 ,分别是_NSConcreteAutoBlock
_NSConcreteFinalizingBlock
_NSConcreteWeakBlockVariable
,本次只介绍3种经常使用类型。面试
__NSGlobalBlock__
)__NSMallocBlock__
)__NSStackBlock__
)//循环引用
self.name = @"1234";
self.block = ^{
NSLog(@"%@",self.name);
};
self.block();
复制代码
不少人都知道Block会引发循环引用,如同上面这段代码,当self持有block,block持有self,就产生了循环引用。接下来就介绍3种解决循环引用的方式
一、__block
:咱们只须要在self.name的下面建立一个中介者vc(__block ViewController *vc = self
),而后将block里面的self.name替换成vc.name,用完以后将vc置为nil便可。(俗称中介者模式)
bash
__main_block_impl_0
的构造函数,咱们在往上看能够发现,它其实就是一个结构体对象。而这个构造函数传了两个值
__main_block_func_0
和
__main_block_desc_0_DATA
。
__main_block_func_0
:就是Block所执行的内容,在C++底层则变成了一个函数。
__main_block_desc_0_DATA
:有两个成员:
reserved
和
Block_size
。
__main_block_impl_0
结构体中建立了一个属性int a,而且在函数实现中建立了一个临时变量a,将结构体中的属性赋值给他。因为此次拷贝是值拷贝,因此在函数里面不能对当前的属性进行修改。为了可以改变a的值,要加上
__block
,以下图:
__block
修饰的临时变量在C++中变成了一个结构体
__Block_byref_a_0
。而且在调用函数的时候,传的是a的指针,而且在函数的实现中,
__Block_byref_a_0 *a = __cself->a
则是进行一次指针拷贝,因此用
__block
修饰的变量能够在Block内部进行修改。
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {
// Its a stack block. Make a copy.
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#endif
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
result->isa = _NSConcreteMallocBlock;
return result;
}
}
复制代码
这里就是当前Block从栈->堆的过程。 一、aBlock->flags & BLOCK_NEEDS_FREE
会判断当前Block的引用计数器,由于block的引用计数器是不受runtime下层处理的,因此它由本身来进行管理。并且这里的引用计数器是+2,而不是+1,由于+1会对别的属性有影响,因此这里是+2。函数
aBlock->flags & BLOCK_IS_GLOBAL
判断当前的Block是否为全局变量,是的话就直接返回
_NSConcreteMallocBlock
。
一、解决block循环引用的思路就是中介者模式。
二、Block的本质就是结构体
三、当Block捕获到外界变量时,他就会从全局block变成堆blockui