Objective-C Block数据类型讲解

本文深入讲解Objective-C中的Block概念,包括Block的定义、使用方法及其内存管理机制。探讨了Block如何处理外部变量,并解决了常见的内存问题,如只读变量及循环持有等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自: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;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值