1、为什么用copy
block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,
2、
__block和__weak有什么区别?
__block
不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
__weak
只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
__block
对象可以在block中被重新赋值,__weak
不可以。
3、循环引用:
循环引用就是当self 拥有一个block的时候,在block 又调用self的方法。形成你中有我,我中有你,谁都无法将谁释放的困局。
self.myBlock = ^{
[self doSomething];
};
+-----------+ +-----------+
| self | | Block |
---> | | --------> | |
| retain 2 | <-------- | retain 1 |
| | | |
+-----------+ +-----------+
又或者
ClassA* objA = [[[ClassA alloc] init] autorelease];
objA.myBlock = ^{
[self doSomething];
};
self.objA = objA;
+-----------+ +-----------+ +-----------+
| self | | objA | | Block |
| | --------> | | --------> | |
| retain 1 | | retain 1 | | retain 1 |
| | | | | |
+-----------+ +-----------+ +-----------+
^ |
| |
+------------------------------------------------+
解决方法:
__weak typeof (self) weakSelf = self;
附:
1、delegate与环
//ClassA: @protocol ClssADelegate - (void)fuck; @end @interface ClassA : UIViewController @property (nonatomic, strong) id delegate; @end //ClassB: @interface ClassB () @property (nonatomic, strong) ClassA *classA; @end @implementation ClassB - (void)viewDidLoad { [super viewDidLoad]; self.classA = [[ClassA alloc] init]; self.classA.delegate = self; }
如上代码,B强引用A,而A的delegate属性指向B,这里的delegate是用strong修饰的,所以A也会强引用B,这是一个典型的循环引用样例。而解决其的方式大家也都耳熟能详,即将delegate改为弱引用。
2、block与环
@interface ClassA () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, assign) NSInteger tem; @end @implementation ClassA - (void)viewDidLoad { [super viewDidLoad]; self.block = ^{ self.tem = 1; }; }
如上代码,self持有block,而堆上的block又会持有self,所以会导致循环引用,这个例子非常好,因为xcode都能检测出来,报出警告:[capturing self strongly in this block is likely to lead to a retain cycle],当然大部分循环引用的情况xcode是不会报警告的。解决这种循环引用的常用方式如下(这种解决方式可以解决大部分block引起的循环引用,但是有一定缺陷,且看下一节):
@interface ClassA () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, assign) NSInteger tem; @end @implementation ClassA - (void)viewDidLoad { [super viewDidLoad]; __weak typeof(self) weakSelf = self self.block = ^{ weakSelf.tem = 1; }; }
3、结论
如上delegate和block引起的循环引用的处理方式,有一个共同的特点,就是使用weak(弱引用)来打破环,使环消失了。所以,可以得出结论,我们可以通过使用将strong(强引用)用weak(弱引用)代替来解决循环引用。
四、解决block循环引用的深入探索
1、weakSelf与其缺陷
//ClassB是一个UIViewController,假设从ClassA pushViewController将ClassB展示出来 @interface ClassB () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, strong) NSString *str; @end @implementation ClassB - (void)dealloc { } - (void)viewDidLoad { [super viewDidLoad]; self.str = @"111"; __weak typeof(self) weakSelf = self; self.block = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", weakSelf.str); }); }; self.block(); }
这里会有两种情况:
若从A push到B,10s之内没有pop回A的话,B中block会执行打印出来111。
若从A push到B,10s之内pop回A的话,B会立即执行dealloc,从而导致B中block打印出(null)。这种情况就是使用weakSelf的缺陷,可能会导致内存提前回收。
2、weakSelf和strongSelf
@interface ClassB () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, strong) NSString *str; @end @implementation ClassB - (void)dealloc { } - (void)viewDidLoad { [super viewDidLoad]; self.str = @"111"; __weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(self) strongSelf = weakSelf; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", strongSelf.str); }); }; self.block(); }
我们发现这样确实解决了问题,但是可能会有两个不理解的点。
这么做和直接用self有什么区别,为什么不会有循环引用:外部的weakSelf是为了打破环,从而使得没有循环引用,而内部的strongSelf仅仅是个局部变量,存在栈中,会在block执行结束后回收,不会再造成循环引用。
这么做和使用weakSelf有什么区别:唯一的区别就是多了一个strongSelf,而这里的strongSelf会使ClassB的对象引用计数+1,使得ClassB pop到A的时候,并不会执行dealloc,因为引用计数还不为0,strongSelf仍持有ClassB,而在block执行完,局部的strongSelf才会回收,此时ClassB dealloc。
这样做其实已经可以解决所有问题,但是强迫症的我们依然能找到它的缺陷:
block内部必须使用strongSelf,很麻烦,不如直接使用self简便。
很容易在block内部不小心使用了self,这样还是会引起循环引用,这种错误很难发觉。
3、@weakify和@strongify
查看github上开源的libextobjc库,可以发现,里面的EXTScope.h里面有两个关于weak和strong的宏定义。
// 宏定义 #define weakify(...) \ ext_keywordify \ metamacro_foreach_cxt(ext_weakify_,, __weak, __VA_ARGS__) #define strongify(...) \ ext_keywordify \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wshadow\"") \ metamacro_foreach(ext_strongify_,, __VA_ARGS__) \ _Pragma("clang diagnostic pop") // 用法 @interface ClassB () @property (nonatomic, copy) dispatch_block_t block; @property (nonatomic, strong) NSString *str; @end @implementation ClassB - (void)dealloc { } - (void)viewDidLoad { [super viewDidLoad]; self.str = @"111"; @weakify(self) self.block = ^{ @strongify(self) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", self.str); }); }; self.block(); }
可以看出,这样就完美解决了3中缺陷,我们可以在block中随意使用self。