我们在block中,遇到引用外部变量,一般习惯性的给这个变量加个block 和weak 。比如block中用到self,一般都会这样写,
__block __typeof(self) weakSelf = self;
那是不是所有的block都要用这样写的。这样写固然万无一失吧,但是面试的时候别人问你,你就懵了,看下面代码
void (^TestBlock)(void) = ^{ NSLog(@"test:%@", self); }; TestBlock();
如果面试者问你,这个TestBlock里面的self,是否会被循环引用呢,为什么?
答案是不会?为什么呢?这要从block的三种类型说起。根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。
NSGlobalBlock如下,我们可以通过是否引用外部变量识别,未引用外部变量即为1.NSGlobalBlock,可以当做函数使用。
float (^sum)(float, float) = ^(float a, float b){ return a + b; }; NSLog(@"block is %@", sum); //block is <__NSGlobalBlock__: 0x47d0>
2.NSStackBlock,前提是MRC环境,arc
void (^TestBlock)(void) = ^{ NSLog(@"test:%@", self); }; NSLog(@"block is %@", ^{ NSLog(@"test:%@", self); }); //block is <__NSStackBlock__: 0xbfffdac0> //打印可看出block是一个 NSStackBlock, 即在栈上, 当函数返回时block将无效 NSLog(@"block is %@", TestBlock); //block is <__NSMallocBlock__: 0x75425a0> //上面这句在非arc中打印是 NSStackBlock, 但是在arc中就是NSMallocBlock //即在arc中默认会将block从栈复制到堆上,而在非arc中,则需要手动copy.
3.NSMallocBlock只需要对NSStackBlock进行copy操作就可以获取
所以说block的循环引用问题,是因为block在拷贝到堆上的时候,会retain其引用的外部变量,那么如果block中如果引用了他的宿主对象,即self,就会能引起循环引用。
留个问题,BLOCK被另一个BLOCK使用时,另一个BLOCK被COPY到堆上时,被使用的BLOCK此时会被copy吗?其参数的BLOCK是否会发生COPY的.