阅读更多
转自:http://kan.weibo.com/con/3516583830745467
/**
用来内存管理第一部分的测试函数
@param value1 第一个整型值
@param num2 第二个参数
*/
int _testBlock(constint value1,int num2);
int _testBlock(constint value1,int num2)
{
return value1 + num2;
}
int main(int argc, constchar * argv[])
{
//Block 数据类型
//1、声明Block 数据类型变量
int num1 = 7;
/**
Block 数据类型与结构体类型相似指的都是泛指类型的数据类型,即需要声明具有实际意义的Block数据类型或结构体类型
int(^aBlock)(int num2),第一个int指的是当前Block数据类型的返回值的类型是整型,可以换成其他数据类型,^aBlock在小括号内表示的数据类型为aBlock,第二个int num2即为参数
^(int num2){
return num1+num2;
}; 赋值运算符的后边,即为Block变量的值,大括号内是函数实现的逻辑,即可以随意更改为其他逻辑,例如return num1 * num2等等;
*/
int(^aBlock)(int num2) = ^(int num2){
return num1+num2;
};
//同样可以通过类型定义,定义具体的Block数据类型,下面即定义一个返回值为整型,只有一个整型参数的myBlock数据类型
typedefint(^myBlock)(int num2);
//可以通过myBlock数据类型声明变量
myBlock bBlock = ^(int num2){
return num1 - num2;
};
//使用第一种方式定义的Block变量
NSLog(@"aBlock = %d",aBlock(3));
//使用第二种方式定义的Block变量
NSLog(@"bBlock = %d",bBlock(3));
//2、Block 变量涉及到的内存问题
int value1 = 2;
//定义具体的Block数据类型CaculateBlock
typedefint(^CaculateBlock)(int num2);
CaculateBlock _block = ^(int num2){
//value1++是不可以改变的
//在Block变量的内部,使用外部的变量时是只读的
return value1 + num2;
};
/**
重新修改valu1的值,结果等于4 而不是5
我们可以通过另外一种方式理解,再main函数外定义一个函数 int _testBlock(const int value1,int num2);
*/
//想想为什么_testBlock一定要在上边调用?
NSLog(@"_testBlock = %d",_testBlock(value1,2));
value1 = 3;
NSLog(@"_block = %d",_block(2));
//怎么解决这种问题只读变量的问题呢,使用__block关键字
__blockint value2 = 2;
CaculateBlock _block2 = ^(int num2){
value2 ++;
return value2 - num2;
};
value2++;
//4-3 = 1,value2是怎么变成4的呢,很明显是value2的两次自增变化的
NSLog(@"_block2 = %d",_block2(3));
//总结内存问题,就是几句话,每一个Block变量是存储在一个栈区stack1(因为是局部变量的原因),但是因为每一个Block变量的值是一段代码块,所以Block变量的值自己也负责管理一片栈区stack2的内存,当Block变量出栈后,Block变量的值自己负责的栈也会自动出栈。
//那么声明Block数据类型的变量的过程中,使用外部变量的时候,使用__block关键字的话,该外部变量可以理解为一个小型的全局变量,但是全局仅限于Block变量中使用,否则不使用__block关键字的话,在声明Block数据类型时,是将该外部变量的值赋值到Block变量自己负责的栈区内,并设置为const只读变量,所以及时修改了外部变量的值,仍然不会影响到Block自己负责的栈区的只读变量的值,因为它们已经是两片内存区域了。
//3、由于使用Block引起的retain cycle即循环持有的问题.
//举例,封转一个学生类FOStudent
/**
//定义一个具体的Block数据类型
typedef int(^StudentBlock) (int _age);
@interface FOStudent : NSObject
//实例属性要写copy,使用copy关键字的作用就是将任意一片内存区域的内容,赋值相同的一份到堆内存
@property (nonatomic ,copy) StudentBlock block;
//年龄,一个测试属性
@property (nonatomic ,assign) int age;
@end
@implementation FOStudent
@synthesize block;
@synthesize age;
- (void)dealloc
{
//在delloc中,在控制台打印delloc消息,提示我们该实例对象已被销毁
NSLog(@"%s",__FUNCTION__);
self.block = nil;
self.age = 0;
[super dealloc];
}
@end
*/
//实例化学生对象
FOStudent *student = [[FOStudentalloc] init];
StudentBlock _studentBlock = ^(int _age){
return student.age + _age;
};
//设置student的block实例属性为刚刚声明的_studentBlock局部变量,因为实例属性使用的是copy关键字,所以局部变量_studentBlock会赋值到堆内存中,并且生命周期与student实例对象相同(因为是在dealloc中释放的实例对象block内存),
student.block = _studentBlock;
//当对student实例对象发送release消息后,发现并没有delloc内容在控制台输出
[student release],student = nil;
//说明student实例对象并没有销毁内存,到底是在哪里它的引用计数器又加1了呢,根据第二部分的理解,声明Block变量的过程中,如果使用到的外部变量没有使用__block关键字,是会拷贝一份到Block变量自己负责的栈区,同理,在OC对象作为外部变量使用到Block变量中的时候,会对该对象的引用计数器+1,并且是在出栈的时候引用计数器-1,因为该block变量已经作为student实例对象的属性与student的声明周期相同,结果student对象不销毁,block不销毁,block不销毁,也就不会出栈,结果造成了retain cycle问题.
//怎么解决问题呢?
//仍然是使用__block关键字,因为__block关键字会定义小型的全局变量
FOStudent *_student = [[FOStudentalloc] init];
__blockFOStudent *bStudent = _student;
StudentBlock _bstudentBlock = ^(int _age){
return bStudent.age + _age;
};
_student.block = _bstudentBlock;
//这样通过这种方式的输出,_student对象就可以销毁了,并且也在控制台进行了输出
[_student release],_student = nil;
return0;
}