Block结构
Apple 提供的Block结构
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
struct Block_layout {
void *isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 *descriptor;
ARC
在开启ARC后,block的内存会比较微妙。ARC会自动处理block的内存,不用手动copy/release。
但是,和非ARC的情况有所不同:
void (^aBlock)(void);
aBlock = ^{
printf("ok");
};
block是对象,所以这个aBlock默认是有__strong修饰符的,即aBlock对该block有strong references。即aBlock在被赋值的那一刻,这个block会被copy。所以,ARC开启后,所能接触到的block基本都是在堆上的。。
void (^aBlock)(void) = nil;
if (!aBlock) {
aBlock = ^{ printf("hehe"); };
}
//block此时block已经被释放,该处留下了一个dangling pointer
aBlock();
上面这个例子,如果是非ARC时,block还在栈帧上,所以没问题。但开启ARC后,block会被先copy到堆上,然后再被释放,这里就会crash了。所以这时就必须手动调用Block_copy了。苹果建议尽量避免这种情况。
循环引用
当block被copy之后(如开启了ARC、或把block放入dispatch queue),该block对它捕获的对象产生strong references (非ARC下是retain),
所以有时需要避免block copy后产生的循环引用。
如果用self引用了block,block又捕获了self,这样就会有循环引用。
因此,需要用weak来声明self
- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; //捕获到的是弱引用
}
}
如果捕获到的是当前对象的成员变量对象,同样也会造成对self的引用,同样也要避免。
- (void)configureBlock {
id tmpIvar = _ivar; //临时变量,避免了self引用
self.block = ^{
[tmpIvar msg];
}
}
为了避免循环引用,可以这样理解block:block就是一个对象,它捕获到的值就是这个对象的@property(strong)。这样在遇到问题时,就能迅速确定是否有循环引用了。Xcode5已经能自动发现这种问题了,不错~