Block内的变量:
一:局部变量
声明block之后,调用blcok之前,对局部变量进行修改,在调用block时,局部边变量的值是修改之前的旧值。
在Block定义时便是将局部变量的值(相当于block拷贝了一份变量的值)传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改并不会影响Block内部的值,同时内部的值也是不可以修改的。
__block修饰:
在局部变量之前使用__block修饰,在Block定时便是将局部变量的指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
二:全局变量
全局变量所占用的内存只有一份,供所有函数共同调用,在Block定义时并没有将全局变量的值或指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
三:全局静态变量
在Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
四:局部静态变量
【静态局部变量:
1,静态局部变量再静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(动态局部变量)属于动态存储类别,存储在动态存储区空间,函数调用结束后即释放。
2,为静态局部变量赋初值是在编译时进行的,即只赋初值一次,在程序运行时它已有初值,以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值,而为自动变量赋初值,不是在编译时进行,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
3,如果在定义局部变量时不赋初值的话,对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符型变量)。而对自动变量来说,如果不赋初 值,则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的值是不确定的。
4,虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的,也就是说,在其他函数中它是“不可见”的。
】
静态局部变量是存储在静态数据存储区域的,也就是和程序拥有一样的生命周期,也就是说在程序运行时,都能够保证block访问到一个有效的变量。但是其作用范围还是局限于定义它的函数中,所以只能在block通过静态局部变量的地址来进行访问。
__block的作用:
__block在MRC下的作用:
1,允许在Block中访问和修改局部变量。
2,禁止Block对所引用的对象进行隐式retain操作。
__block在ARC下的作用
1,允许在Block中访问和修改局部变量。(可以用__block修饰后,在block里将引用的对象 = nil,也是避免循环引用的一种方法。)
循环引用
1,在Block的内存存储在堆中时,如果在Block中引用了外面的对象,会对所引用的对象进行强引用,但是在Block被释放时会自动去掉对该对象的强引用,所以不会造成内存泄露。
#import <Foundation/Foundation.h>
typedef void(^myBlcok)();
@interface Person : NSObject
@property (nonatomic,copy)myBlcok myblock;
@property (nonatomic,assign)NSInteger age;
- (void)resetBlock;
@end
#import "Person.h"
@implementation Person
- (void)dealloc
{
NSLog(@"----Person释放了");
}
- (void)resetBlock{
__weak typeof(self) weakSelf = self;
self.myblock = ^(){
NSLog(@"---%@",weakSelf);//不用weakSelf,就循环引用了;
};
}
2,如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用。因为Block用copy修饰符修饰,(这样才能保证Block在堆里,以免Block在栈中被系统释放)所以Block会对Person对象进行一次强引用,导致循环引用无法释放。
Person *p2 = [[Person alloc]init];
p2.myblock = ^(){
// NSLog(@"----%p",p2);//循环引用了
};
p2.myblock();
}
Person *p3 = [[Person alloc] init];
[p3 resetBlock];//Person对象在这里无法正常释放,在resetBlock方法实现中,Block内部对self进行了一次强引用,导致循环引用无法释放
//解决办法:
//如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用,解决循环引用的办法是使用一个弱引用的指针指向该对象,然后在Block内部使用该弱引用指针来进行操作,这样避免了Block对对象进行强引用
Person *p4 = [[Person alloc]init];
__weak typeof(p4) weakP4 = p4;
p4.myblock = ^(){
NSLog(@"weakp4---%@",weakP4);
};
p4.myblock();
在Block内部定义的变量,会在作用域结束时自动释放,Block对其并没有强引用关系,且在ARC中只需要避免循环引用即可,如果只是Block单方面地对外部变量进行强引用,并不会造成内存泄漏。
Block的类型
MRC下:有三种类型的block,NSStackBlock, NSGlobalBlock 、NSMallocBlock
ARC下,仅存在 NSGlobalBlock 、NSMallocBlock 两种block
在ARC中,当block引用外部变量时(不管是值类型的变量,还是对象类型的变量),block会存在堆中,为NSMallocBlock类型,如果不引用外部变量,block就位于全局区,为NSGlobalBlock类型。
在Block的内存存储在堆中时,如果在Block中引用了外面的对象,会对所引用的对象进行强引用,但是在Block被释放时会自动去掉对该对象的强引用,所以不会造成内存泄漏。
如图,如果不在block里用一个强引用strongSelf引用弱引用,那么,在block执行结束后,weakSelf会自动置为nil,所以延时方法里的 代码打印出来为nil,用strongSelf的话, 就不会在block执行完毕后自动置为nil,而是在程序结束后。
当block作为参数传递时,手动copy一份,把block copy到堆中,避免是NSStackBlock类型的block,释放后,还在使用。