OC-Block

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已经能自动发现这种问题了,不错~

猜你喜欢

转载自blog.csdn.net/IOSNEUSOFT/article/details/84967451