lijiexiaoge 2019-06-27
最近的面试中,被问到各种各样的问题,有的问题真的是一脸懵逼。Block的调用,有的时候回产生循环引用,与及如何解除循环引用,做iOS开发的,想必大家都知道。然而最近被问到这样一个问题,在block内部申明使用static变量,会造成循环引用吗?第一反应是,自己没有这么做过,也没有见别人这样写过,哪怕是开源的框架里,目前也没有这么写的。于是和小伙伴们讨论了一下,也敲代码验证了一下。
当两个不同的对象各有一个强引用指向对方,那么循环引用变产生了,当然一个对象产生的环也是一样的。
从引用计数来说,如果两个对象互相引用的时候,双方的引用计数都是+1的,导致如何时候引用计数都不为0,始终无法释放他们的内存,即使已经没有变量持有它。
static关键字声明的变量必须放在implementation外面,或者方法中,如果不为它赋值默认为0,它只在程序开机初始化一次。先得说下内存中和变量有关的分区:堆、栈、静态区。其中,栈和静态区是操作系统自己管理的,对程序员来说相对透明,所以,一般我们只需要关注堆的内存分配,而循环引用的产生,也和其息息相关,即循环引用会导致堆里的内存无法正常回收。
- (void)testBlockWithStaticVariable { static NSInteger num1 = 100; __block NSInteger num2 = 5; void (^block)(void) = ^{ static NSInteger num3 = 10; NSInteger num4 = 0; NSLog(@"the current value is num1 = %@, num2 = %@, num3 = %@, num4 = %@",@(num1++),@(num2++),@(num3++),@(num4++)); //[self testRecycle]; //可以正常调用,不会发生循环引用 }; for(NSInteger i = 0; i < 10; i++){ block(); } }
从结果看num1、num2、num3都进行了累加操作,只有内部申请的普通变量num4没有进行叠加,因为它是放在栈上的,每次都初始化。num1、num3都是static的,无论是在block内部调用外部的static变量,还是在block内部申请使用static变量,都不造成循环引用,也不会发生内存泄漏。
在block内部调用当前类的方法,不会发生循环引用。
- (void)testRecycle { static NSInteger num1 = 100; __block NSInteger num2 = 5; self.testBlock = ^{ static NSInteger num3 = 10; NSInteger num4 = 0; NSLog(@"the current value is num1 = %@, num2 = %@, num3 = %@, num4 = %@",@(num1++),@(num2++),@(num3++),@(num4++)); NSLog(@"----------------"); //[self testBlockWithStaticVariable]; //会发生循环引用 Capturing 'self' strongly in this block is likely to lead to a retain cycle }; for(NSInteger i = 0; i < 10; i++){ self.testBlock(); } }
打印结果和上图一样,唯一不同的是,在该block内部调用当前类的方法,系统会报Capturing 'self' strongly in this block is likely to lead to a retain cycle的错误。
block内部申明static变量,不会内存泄露,也不会循环引用。