关于block的一些需要注意的地方

来源:互联网 发布:ubuntu怎么打开exe 编辑:程序博客网 时间:2024/05/16 03:01

一、定义block类型属性的时候该用copy、assign还是retain?

block代表的是一个代码块,块是不能保留的,对块发送retain是没有意义的。所以block中并没有像提供Block_copy和Block_release那样提供Block_retain这样的方法,而且objc里面的retain消息发送给block对象后,其内部实现是什么都不做,无效。

另外,block块默认是在在栈上分配的, 所以一旦离开作用域, 就会释放, 因此如果要把块用在别的地方, 必须要复制一份.同样的道理,在一个类中定义一个block类型的属性,该属性的赋值操作和执行该block一般情况下是不同时的,而且也不在相同的方法里,所以在赋值的时候就需要用copy,将block复制到堆上保留。

二、关于__block关键字

用__block关键字修饰局部变量,然后在block就可以随意访问,且是可读写的。否则局部变量只会被当成常量被编译器编译进block里。

三、block的复制行为

block的两种复制方式:[__block copy][Block_copy __block;两种释放方式:[__block release][Block_release __block;

  对block调用复制,有以下几种情况

a、对全局区的block调用copy,会返回原来block的指针,并不会进行复制操作,而且在这期间不处理任何东西,相当于对全局的block调用copy是无效的

b、对栈上的block调用copy,每次会返回新复制到堆上的block的指针,同时,block中引用到的所有__block变量都会被复制至堆一份(多次拷贝,只会生成一份,计数器会被多次加1)。这块需要注意:对同一个blok进行多次调用copy,会返回多个新复制到堆上的block指针,相当于在堆上就有了多份,但是block中用到的以__block标记的局部变量只会被复制到堆上一份,这一份供所有block使用,多次copy,只会增加堆上该拷贝的计数器.

c、对已经位于堆上的block,再次或者多次调用copy,只会增加block的引用计数,不会重新复制。

四、block中的循环引用

typedef NSString *(^stringName)(NSString *,NSInteger);

@interfaceBlockObject()

{

   NSInteger count;

}


@property (nonatomic,retain) NSObject *obj;

@property (nonatomic,assign) NSInteger flag;


@property (nonatomiccopystringName myName;

@end




@implementation BlockObject

@synthesize obj;

@synthesize flag;

@synthesize myName;


- (void)test

{

    BOOL ret =YES;

   self.myName = ^(NSString *name,NSInteger age){

        

        NSLog(@"count = %d",count);//会引起循环引用

        NSLog(@"self.obj = %@",self.obj);//会引起循环引用                      

NSLog(@"flag = %d",flag);//会引起循环引用

       NSLog(@"self = %@",self);//会引起循环引用

NSLog(@"ret = %d",ret);//不会引起循环引用

        

       return @"";

    };

}

关于这段代码片段的解释:block中应用到self对象或者self对象的属性才会导致循环引用,而使用局部变量则不会出现循环引用。为什么呢?block中使用self对象或者self对象的属性,self会被block retain一次,计数器加1,这时候block就持有了self;反过来,block类型的myName属性是self的一个属性,所以self持有了myname,这样就导致了循环引用。最终结果是:myName属性是等到self被dealloc是才会区释放,而self被block retain了,只有等到block释放了堆self的引用,self才会被释放,这就悲剧了。。。。。

要想打破这种循环引用,必须手动的打破它。打破循环无非就两中途径:打破self对block的引用或者打破block对self的引用。

a、尝试打破self对block的引用:将myName的修饰从copy改为assign,这样就不再持有block,但是当test方法执行完之后block就会被释放(因为该block是在栈上创建的),这样后续这个block就无法使用,这种方法不可行。

b、尝试打破block对self的引用:之前提到了关键字__block,它修饰的对象, Block不会对其复制, 仅仅使用, 不会增加引用计数.

所以可以这样修改:

- (void)test

{

    BOOL ret = YES;


__block id blockSelf=self;

__blockNSInteger indexBlock =count;

    self.myName = ^(NSString *name,NSInteger age){

NSLog(@"indexBlock = %d",indexBlock);

        NSLog(@"self.obj = %@",blockSelf.obj);                     

NSLog(@"flag = %d",blockSelf.flag);

        NSLog(@"self = %@",blockSelf);

NSLog(@"ret = %d",ret);


        return @"";

    };

}

原创粉丝点击