1、block做为属性为什么使用copy
首先,要明确的是在ARC下block作为属性采用copy和strong策略效果是一样的,标题这句话只适用于MRC,在ARC下任然使用copy可以认为是一种习惯,没有什么特殊意义,用strong也不会错。
- 1)所谓的外部变量引用(变量捕获)
是指block在使用到block的{}之外定义的变量(外部变量)时,会将外部变量拷贝到堆上,通常情况下这里的拷贝只是简单的值传递,只有当该变量用__block
修饰的时候才是址传递,所以如果想要在block内重新赋值给外部变量都需要__block
修饰(但是当捕获的外部变量是全局变量,静态变量,或者捕获到的变量是某个对象的成员变量或者属性除外)
NSMutableArrary *arrary = [NSMutableArrary arrary];
void(^block)() = ^{
[arrary addObject:@"呵呵呵"];
};
block();
NSLog("%@",arrary);
//输出'("呵呵呵")'
Person *person = [[Person alloc] init];
void(^block1) = ^{
person.name = @"葛二蛋";
};
block1();
NSLog("%@",person.name);
//输出'葛二蛋'
self.count = 10;
void(block2) = ^{
self.count = 10000;
};
block2();
NSLog("%zd",self.count);
- 2)block的存储区域
block有3种类型
__NSGlobalBlock__
类型block存储在全局区
__NSStackBlock__
类型的block存储在栈区
__NSMallocBlock__
类型的block存储在堆区
在MRC的下默认的block是__NSGlobalBlock__
类型,引用了外部变量 (非全局变量和静态变量)的block是__NSStackBlock__
类型,对__NSStackBlock__
类型的block做copy操作,block将会被拷贝到堆区变成__NSMallocBlock__
类型,而__NSGlobalBlock__
类型的block做copy操作是无效的。
在ARC下会默认的block也是__NSGlobalBlock__
类型,引用了外部变量(非全局变量和静态变量)的block也是__NSStackBlock__
类型,同样对__NSGlobalBlock__
类型的block做copy操作也是无效的。
ARC和MRC的block的区别在于ARC下的block在赋值的时候编译器会对strong和copy策略的block自动做copy操作后赋值,所以ARC的block作为属性的时候只需要采用strong或者copy策略,引用了外部变量的block就会是__NSMallocBlock__
类型的在堆区存储,而没有采用strong或者copy策略和没有引用外部变量的block就是__NSGlobalBlock__
类型的存储在全局区域。
2、block书写习惯
- 1)代码简洁
block的等号右边尽量不要直接写出实现,建议创建新方法,在实现中调用创建的新方法,这样代码会显得更加简洁。比如在UITableviewcell中做回调的方法block,如果实现代码非常的长,而且cell的回调block不止一个,如果直接在UITableview的代理方法中实现block回调我们的代码将会很可怕。 - 2)线程安全
当block为nil的时候,直接调用block会造成崩溃,我们通常需要在调用block之前判空。但是由于block很可能会在我们判空之后,执行之前,被其他线程置空为nil,所以最保险的方法是,在判空之前将block用一个本地的临时变量缓存一份,然后再进行判空操作,就会避免多线程操作的不安全。 - 3)循环引用
通常为了避免block循环引用,我们会用weakself代替self,但是在多线程操作的时候,最好是将weakself做一次强引用,避免self在执行block操作之前被释放掉,一旦被释放self会被自动置为nil,此时block调用可能会没有反应。
这篇大牛的文章很不错http://www.jianshu.com/p/ee9756f3d5f6
网友评论