您的位置:首页 > 其它

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

2013-09-16 17:22 274 查看
一、定义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);

@interface
BlockObject()
{
   
NSInteger count;
}

@property (nonatomic,
retain) NSObject *obj;

@property (nonatomic,
assign) NSInteger flag;

@property (nonatomic, copy) stringName 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;

__block
NSInteger 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 @"";
    };
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: