block 是在 iOS 4 中引入的新特性,苹果官方推荐使用这种做法。
block 注意事项
1,block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝。
如下代码:
-(void)testVariable
{
NSInteger outsideVariable = 10;
//__block NSInteger outsideVariable = 10;
NSMutableArray * outsideArray = [[NSMutableArray alloc] init];
void (^blockObject)(void) = ^(void){
NSInteger insideVariable = 20;
NSLog(@" > member variable = %d", self.memberVariable);
NSLog(@" > outside variable = %ld", (long)outsideVariable);
NSLog(@" > inside variable = %ld", (long)insideVariable);
[outsideArray addObject:@"AddedInsideBlock"];
};
outsideVariable = 30;
self.memberVariable = 30;
blockObject();
NSLog(@" > %lu items in outsideArray", (unsigned long)[outsideArray count]);}
打印结果如下:
注意到outside 变量的输出值为10,这是因为blockObject 在实现时会对 outside 变量进行只读拷贝,在 block 块内使用该只读拷贝。因此这里输出的是拷贝时的变量值 10。如果,我们想要让 blockObject 修改或同步使用 outside 变量就需要用 __block 来修饰 outside 变量。
**注意**
a),在上面的 block 中,我们往 outsideArray 数组中添加了值,但并未修改 outsideArray 自身,这是允许的,因为拷贝的是 outsideArray 自身。
b),对于 static 变量,全局变量,在 block 中是有读写权限的,因为在 block 的内部实现中,拷贝的是指向这些变量的指针。
c), __block 变量的内部实现要复杂许多,__block 变量其实是一个结构体对象,拷贝的是指向该结构体对象的指针。
2 ,使用 weak–strong dance 技术来避免循环引用
在block中调用self很可能会引起循环引用,在block中需要对weakSelf进行strong,保证代码在执行到block中,self不会被释放,当block执行完后,会自动释放该strongSelf。
先看这个例子:
NSArray *anArray = @[@"1", @"2", @"3"];
[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[self doSomething:idx];
}];
这种情况下,block中retain了self,当block中的代码被执行完后,self就会被ARC释放。所以不需要处理weakself的情况。
该VC被销毁后会有delloc打印。
再来看一个例子:
NSArray *anArray = @[@"1", @"2", @"3"];
[anArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[self dosomething:obj];
}];
// __weak SecondViewController *weakself = self;
self.aBlock = ^(id obj, NSUInteger idx, BOOL *stop){
[self dosomething:obj];
};
这种情况编译器会包警告提示这儿的self:
并且不会有delloc的打印。
这个例子的区别在于:block被self strong引用。所以结果就是block中引用了self,self引用了block。那么这个时候,如果你不使用weakself,则self和block永远都不会被释放。
那么是不是遇到block都要使用weakself呢?当然不是,而且如果全部使用weakself,会出现你想执行block中的代码时,self已经被释放掉了的情况。
另外,在处理weakself时,有两种做法:__weak和__unsafe_unretained。两种做法各有推荐,
还有一种比较常用的方法,这个方法比较通用:
/**
* 强弱引用转换,用于解决代码块(block)与强引用self之间的循环引用问题
* 调用方式: `@weakify_self`实现弱引用转换,`@strongify_self`实现强引用转换
*
* 示例:
* @weakify_self
* [obj block:^{
* @strongify_self
* self.property = something;
* }];
*/
#ifndef weakify_self
#if __has_feature(objc_arc)
#define weakify_self autoreleasepool{} __weak __typeof__(self) weakSelf = self;
#else
#define weakify_self autoreleasepool{} __block __typeof__(self) blockSelf = self;
#endif
#endif
#ifndef strongify_self
#if __has_feature(objc_arc)
#define strongify_self try{} @finally{} __typeof__(weakSelf) self = weakSelf;
#else
#define strongify_self try{} @finally{} __typeof__(blockSelf) self = blockSelf;
#endif
#endif
/**
* 强弱引用转换,用于解决代码块(block)与强引用对象之间的循环引用问题
* 调用方式: `@weakify(object)`实现弱引用转换,`@strongify(object)`实现强引用转换
用的时候就:
@weakify(self) 外部weak
@strongify(self) 内部strong
这样就避免了循环引用了,dealloc打印如下:
2016-05-18 11:36:31.531 lianxi[10219:1055254] *****
dealloc
No retain cycle