block即代码块,和其他属性类似,只是block存储的是函数体。block执行的代码,是在编译时已经生成好的了。首先来看一下block的内存模型:

struct Block_descriptor {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy)(void *dst, void *src);
    void (*dispose)(void*);
};

struct Block_layout {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
/* Imported variables */ };
  • isa 指针, 所有对象都有改指针,用于实现对象相关的功能。
  • flags,用于按bit位表示一下block的附加信息。
  • reserved,保留变量。
  • invoke,函数指针,指向具体的block实现的函数调用地址。
  • descriptor,表示该block的附加描述信息,主要是size大小,以及copy和dispose函数指针。
  • variables,capture过来的变量,block能够访问它外部的局部变量,就是因为将这些变量(活变量的地址)复制到了结构体中。

 

block使用局部变量的时候,可以访问,但是不可以修改变量的值。如果是访问指针类型,会修改指针所指地址的内容,不会修改指针。如果需要在block内部对局部变量做修改,使用关键字__block。被__block修饰的变量称作block变量。基本类型的block变量等效于全局变量或静态变量。

 

关于block的循环引用的问题:

循环引用是指两个对象互相强引用对方,即retain了对方,从而导致两个对象都无法释放而导致内存泄漏。block中的循环引用问题,是因为block在考呗到堆上的时候,会retain其引用的外部变量,那么如果block中引用了他的宿主对象,那很有可能引起循环引用。而解决的办法就是使用__weak或者__unsafe_unretained来修饰,例如

 1 typedef void(^MyBlock)(void);
 2 @interface MyViewController : UIViewController 
 3 
 4 @property(nonatomic, assign)MyBlock myBlock;
 5 
 6 @end
 7 
 8 
 9 
10 @implementation MyViewController
11 
12 - (void)viewDidLoad{
13     [super viewDidLoad];
14     
15     //系统警告:capturing 'self' strongly in this block is likely to lead to retain cycle,直接导致循环引用
16     self.myBlock = ^{
17         [self func];
18     };
19 
20     //不会引起循环引用
21     __weak typeof(self) weakSelf = self;
22     self.myBlock = ^{
23         [weakSelf doSth];
24     };
25     self.myBlock();
26 }
27 
28 - (void)func{
29     NSLog(@"doSomething");
30 }
31 
32 @end

但是在不会引起循环引用的block实现中,会存在一个问题,这个得从__weak说起。

弱引用为什么不引起循环引用?就是因为弱引用是不加引用计数,这样会产生一定的风险,其引用的对象随时有可能被ARC释放掉。为了解决这个问题,而引入的一个解决方法是:weak-strong dance。代码如下

__weak typeof(self) weakSelf = self;
self.block = ^{
    //加入强引用
    __strong typeof(weakSelf) strongSelf = weakSelf;
    [strongSelf doSth];
};

这样加入强引用,可以避免self在执行block的时候被析构而无法执行。

现在再来看一个问题,如果在添加强引用之前weakSelf已经为nil了,那强引用出来也是nil,再去执行方法同样会出问题,那为了保证方法能正确执行,Apple官方文档建议为:

__weak typeof(self) weakSelf = self;
self.block = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    //如果强引用self不为空才执行
    if (strongSelf) {
        [strongSelf doSth];
    }
};

这就是weak-strong dance的合理做法。

关于__unsafe_unretained, 文档中是这么写的:

In some cases you can use __unsafe_unretained if the class isn’t __weak compatible. This can, however, become impractical for nontrivial cycles because it can be hard or impossible to validate that the __unsafe_unretained pointer is still valid and still points to the same object in question.

意思是:在某些情况下,如果类不支持__weak,可以使用__unsafe_unretained,然而它在实际中仍然无法解决non-trivial cycles问题,因为它很难甚至不可能来保持__unsafe_unretained指针活跃并且仍然指向同一个对象。

 

官方文档传送门:

https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW4

posted on 2016-08-05 16:46  Gary_小咖  阅读(373)  评论(0编辑  收藏  举报