文章主要内容
- block的声明
- block中的循环引用问题
block的声明和赋值
我们使用 ^运算符来声明一个block变量,而且在声明完变量后,要像声明普通变量一样,最后要加 ;
- 声明 block 语法
数据返回值类型 (^变量名) (参数列表) = NULL;
int ^(block)(int) = NULL;
block = ^(int m) {
return m * m;
};
循环引用问题
由于block会持有block中的对象,如果该对象又持有了block,就会造成循环引用。如下
typedef void(^block)();
@property (copy, nonatomic) block myBlock;
@property (copy, nonatomic) NSString *blockString;
- (void)testBlock {
self.myBlock = ^() {
//其实注释中的代码,同样会造成循环引用
NSString *localString = self.blockString;
//NSString *localString = _blockString;
//[self doSomething];
};
}
以上循环引用很容易发现,而发生在多个对象间的时候,往往容易被忽略。如下
//ClassB
@interface ClassB : NSObject
@property (strong, nonatomic) ClassA *objA;
- (void)doSomething;
@end
//ClassA
@property (strong, nonatomic) ClassB *objB;
@property (copy, nonatomic) block myBlock;
- (void)testBlockRetainCycle {
ClassB* objB = [[ClassB alloc] init];
self.myBlock = ^() {
[objB doSomething];
};
objB.objA = self;
}
一些不会造成循环引用的block
开发过程中,发现一些同学并没有完全理解循环引用,以为只要有block的地方就会要用 __weak 来修饰对象,这样完全没有必要,一下几种block是不会造成循环引用的。
-
大部分GCD方法
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
因为self 并没有对GCD的block进行持有,所以没有造成循环引用。
-
block不是属性,而是临时变量
- (void)doSomething {
[self testWithBlock:^{
[self test];
}];
}
- (void)testWithBlock:(void(^)())block {
block();
}
- (void)test {
NSLog(@"test");
}
block使用对象被提前释放
看下面例子,有这种情况,如果不只是ClassA持有了myBlock,ClassB也持有了myBlock。

当ClassA被someObj对象释放后

此时,ClassA对象已经被释放,而myBlock还是被ClassB持有,没有释放;如果myBlock这个时被调度,而此时ClassA已经被释放,此时访问的ClassA将是一个nil对象(使用__weak修饰,对象释放时会置为nil),而引发错误。
另一个常见错误使用是,开发者担心循环引用错误(如上所述不会出现循环引用的情况),使用__weak。比如
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf doSomething];
});
因为将block作为参数传给dispatch_async时,系统会将block拷贝到堆上,而且block会持有block中用到的对象,因为dispatch_async并不知道block中对象会在什么时候被释放,为了确保系统调度执行block中的任务时其对象没有被意外释放掉,dispatch_async必须自己retain一次对象(即self),任务完成后再release对象(即self)。但这里使用__weak,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行block之前,self可能已被销毁,但系统并不知道这个情况,导致block执行时访问已经被释放的self,而达不到预期的结果。
block执行过程中对象被释放
还有一种场景,在block执行开始时self对象还未被释放,而执行过程中,self被释放了,此时访问self时,就会发生错误。
对于这种场景,应该在block中对 对象使用__strong修饰,使得在block期间对 对象持有,block执行结束后,解除其持有。
- (void)testBlockRetainCycle {
ClassA* objA = [[ClassA alloc] init];
__weak typeof(objA) weakObjA = objA;
self.myBlock = ^() {
__strong typeof(weakObjA) strongWeakObjA = weakObjA;
[strongWeakObjA doSomething];
};
objA.objA = self;
}
注:此方法只能保证在block执行期间对象不被释放,如果对象在block执行执行之前已经被释放了,该方法也无效。
参考链接
https://www.jianshu.com/p/1515fb0c3568
https://blog.devzeng.com/blog/ios-arc-block-retain-cycle.htmlhttps://www.jianshu.com/p/492be28d63c4